If you want to follow along the code and slides are available here:
https://git.rushsteve1.us/rushsteve1/rust-talk
According to Wikipedia
Rust is a multi-paradigm systems programming language focused on safety, especially safe concurrency.
Rust is an alternative to many other "systems-languages" such as C, C++, D, Nim, Pony, OCaml, etc.
It was not made to replace these languages but to offer another option.
- Performance: No runtime, no garbage collector, and no wasted memory.
- Reliability: Garunteed memory and thread safety.
- Productivity: Useful errors, and the best tooling around
Rust is for everyone who took Data Structures and now hates C++ with a passion.
Rust is for everyone who wants speed but doesn't want to compromise for it.
Rust is for everyone who likes C but wishes it was more like Haskell.
Rust has an official book detailing it's features and guiding you to learn Rust. It is a fantastice reference and a more in-depth introduction to the language.
Another great resource is the Rust By Example pages which break down topics using real code examples.
Rustup is the recommended way of installing Rust. It can be used to keep up to date, install extra tools, and handle multiple Rust versions at once.
Follow the instrutions on rustup.rs to get started.
Part build tool, part package manager, part everything else.
Since effectively every Rust project uses Cargo that leads to a consistent experience building everything. No more trying to understand Makefiles.
Central repository for Rust packages (called "crates").
The NPM of Rust, and is tightly integrated with Cargo. Rust greatly encourages the use of Crates, even including some in the standard library.
Easy and consistent documentation for all the packages on Crates.io.
Documentation is auto-generated from code, meaning even poorly documented projects have something you can look at.
- Visual Studio Code
- IntelliJ Rust Plugin
- Rust Language Server
And of course the assorted Vim and Emacs plugins.
fn main() {
println!("Hello World!");
}
Rust code is C-style but has some differences so I'm going to quickly go over the basic syntax and ideas.
let var = 10;
let y: String = "Hello!";
let mut m = 10;
m = 15;
Variables are immutable by default and have to be marked as mutable before they can be changed.
Functions work the same as in most languages
fn hello() -> String {
String::new("Hello!")
}
Notice the lack of a semi-colon. That means the value is being returned.
Class-like structures containing multiple values.
struct Message {
text: String,
time: usize
}
Can also have methods, more on that in a sec.
Variants of the same type.
enum YesNo {
Yes,
No
}
Enum variants can be of any type unlike in C.
enum NoYes {
No(String),
Yes(usize)
}
Implement methods on a Struct or Enum.
impl Message {
// Create's a new Message
fn new(text: String) -> Self {
Self {
text,
time: 1553650467
}
}
}
Everything is an expression in Rust which means you can do things like this.
let var = if x > 10 {
x
} else {
20
};
This is a common idea in other functional languages.
Results are used to handle an error state.
fn div(x: f64, y: f64) -> Result<f64, MathError> {
if y == 0.0 {
Err(MathError::DivisionByZero)
} else {
Ok(x / y)
}
}
...
div(10, 0).unwrap(); // Crashes with a MathError
You can have custom errors to handle things as you want
Options can denote a None
state.
fn checked_division(dividend: i32, divisor: i32) -> Option<i32> {
if divisor == 0 {
None
} else {
Some(dividend / divisor)
}
}
...
checked_division(10, 0).unwrap(); // Crashes with a None error
Rust has no null
so the Option is used instead.
You can pattern match any value, but it is especially useful on Strings and Enums.
match checked_division(10, 10) {
Some(x) => println!(x),
None => println!("You can't divide by Zero!")
}
This is a common way of handling errors.
Inheritance in Rust is done through Traits, which garuntee that a Struct provides certain functions.
#[derive(Debug)] // This automatically implements a Trait
Point { x: isize, y: isize }
trait Print {
fn print(&self)
}
impl Print for Point {
fn print(&self) {
println!("x: {} y: {}", self.x, self.y)
}
}
Traits are how you implement things like addition and type conversion for your own structures.
From<(isize, isize)> for Point {
fn from(f: (isize, isize)) -> Self {
Point { x: f.0, y: f.1 }
}
}
This converts a Tuple to a Point.
While Rust does have pointers they are rarely used. Instead Rust has what is known as borrowing.
A variable is owned by one scope and let's others borrow it
In Rust all variables, and more noticeably all borrows, have a lifetime. The compiler is able to determine, or be told, how long a variable is going to be used before it can be freed.
The simplest example is constant variables which need to have the
'static
lifetime.
const VAR: &'static str = "I am constant";
Since everything is immutable by default in Rust there are times when you want to be able to change something but can't for various reasons.
One solution to this is Interior Mutability with types like Cell
let c = Cell::new(10);
c.set(15);
This code is first part of the Final Project of the Rust Book.
You can find the code, and the presentation here https://git.rushsteve1.us/rushsteve1/rust-talk