Hello world in Rust:
fn main() { println!("Hello, world!"); }
To compile binary rustc app.rs.
let greeting = "Hello"; let subject = "World"; // always prints interpolated string println!("{} {}!", greeting, subject);
Other functions that use {} for string interpolation:
// format! let combined_str = format!("{} {}!", greeting, subject); // panic! panic!("{}", combined_str);
panic is a way to basically stop the program.
let x = 2.0; // f64 let y = 2.2; // f64 println!("{}", x + y); // 4.2
Note: macros aren't covered in this course, but basically what they do at compile time is take the arguments into some number of function calls.
// works similar to const in JS let x = 1.1;
By default, cannot reassign let and it is immutable.
let mut x = 1.1; x = 2.2;
This is mutable. By default, Rust likes things to be mutable.
That being said, let mut allows reassignment, but only for that particular type.
let mut y = 2.2; y = 3.1; // allowed y = "three point one"; // not allowed
You can be explicit with types using type annotation:
let x: f64 = 1.1;
You do not need to assign. It can use type inference. It is worth knowing that it won't always get this right.
fn add_floats(x: f64, y: f64) -> f64 { return x + y; }
If your function returns nothing, no return type annotation is required.
Functions come up a lot more than macros.
let x: f64 = 10.0 / 3.0; let y: f32 = 10.0 / 3.0;
There are only two float sizes (
f32andf64).
Why use one of the other? How much space do you need. It is about memory trade-offs.
let ninety = 90; let negative_five = -5; let one_thousand = 1_000; let exactly_three= 10 / 3; // 3 - gets the floor of the division. Be careful with integer division. let this_will_panic = 10 / 0; // will panic
There are different integer types (unsigned and signed) u8, u16, u32, u64, i8, i16, i32, i64.
There is also char which is u32 that's been Unicode validated.
fn multiple(x: i64, y: u8) -> i64 { return x * (y as i64); }
You can also convert ints to floats.
let should_we_go_fast = true; let should_we_go_slow = false; true as u8 // evaluates to 1 1 == 2 // evaluates to false
Under the hood, booleans are represented as
u8s.
Also worth noting, == is structural equality and not referential equality.
if cats > 1 { println!("We have more than one cat!"); } else { println!("We need more cats!"); }
You MUST always use a boolean for the if/else conditional.
An expression evaluates to a value e.g. cats > 1000.
A statement does not. println!("We have more than one cat!"); is a statement.
The distinction matters for getting conveniences when you choose an expression over an statement.
fn multiply_both(x: i64, y: i64) -> i64 { return x * y; } fn multiply_both(x: i64, y: i64) -> i64 { x * y // notice no semi-colon and this will be returned. }
This is important because you can actually do this with if/else statements.
let message = if cats > 1 { "Multiple cats" } else if cats > 1_000 { "Too many cats!" } else { "Need more cats!" }; // need a semicolon because it is a statement
In a comparison, it was almost like this block became a ternary.
Two expressions at the end of a function will end up with a compiler error.
For early returns, use the return key.
Other note:
null,nil,undefined- Rust doesn't have them.