Rust Lang
Table of Contents
1 TODO More In the Book
2 TODO the library reference
3 Why Rust a.k.a. features
Focus:
- Safety
- Speed
- concurrency
- low level control & high level abstraction
What I understand:
- move semantics
- guaranteed memory safety
- threads without data races
- type inference
- minimal runtime
- efficient C bindings
Not understand yet:
- zero-cost abstractions
- trait-based generics
- pattern matching
3.1 TODO know how to describe the feature
4 Reference
- Official website: https://www.rust-lang.org
- The Rust Programming Language (The book): https://doc.rust-lang.org/book/
- Standard library reference: https://doc.rust-lang.org/std/
5 Environment Setup
Install the rust
package in arch linux. The cargo
package is the
package manager for rust. The official site for cargo:
https://crates.io/, is also powered by Rust, so Rust is capable of
writing web applications.
There's another package called rustup
. It is the official rust
installation method. It is a rust toolchain installer. It seems to be
able to manage the version of rust, and also support cross compile.
Rustup method will install both rustc and cargo.
6 Compile
Directly use the compiler by rustc main.rs
, will generate the executable.
Using cargo as build system:
cargo new hello_cargo --bin
: create a project. –bin option specify that we want a executable project instead library one.cargo build
: run this inside the generated hellocargo folder, build the project, intotarget/debug
foldercargo run
: to compile and runcargo build --release
will enable optimization and output totarget/release
folder.
7 Misc
Comment is only //
.
8 Statement and expression
Statement does not have a value. So you cannot assign a let statement to another let. However, experssion has a value. A block is also an expression, and evaluates to the last expression it contains.
let x = 5; let y = (let x = 5); // wrong let y = { // ok let x = 3; x+1 };
9 function
fn main() { // this is the main entry point of program } fn foo() {} fn bar(x: u32, y: i32) -> i32 {5}
The return value is the final expression in the block of the body of the function. Note that the final expression should NOT have a semicolon. The semicolon will make it a statement, thus not able to have a value for return.
Of course, you can have a return statement return 8;
to return
explicitly.
10 import
use directive is only for shortcut. Without use, you can still use it, but need to full name.
std::io::stdin() // using "use" use std::io; io::stdin()
11 variable
11.1 Variable
All variables are immutable by default, but you can make it mutable. The type can be omitted for rust to infer.
let foo = 5; // immutable let mut bar = 5; // mutable
Variable can be shadowed, of course, but in rust, you can shadow a variable in sequencial block. The effect is creating a new variable with the same name, and the type of variable can be changed:
let spaces = " "; let spaces = spaces.len();
11.2 Constant
A constant must be declared with:
- const keyword
- explicit type
- have value not computed at runtime
const foo: u32 = 8;
11.3 Reference
Reference is also immutable by default, so when passing a variable by reference as a parameter to a function, use:
foo(&mut bar)
12 Type
12.1 Scalar type
12.1.1 Integer
length | signed | unsigned |
---|---|---|
8 | i8 | u8 |
16 | i16 | u16 |
32 | i32 | u32 |
64 | i64 | u64 |
arch | isize | usize |
isize and usize will be 64-bit on a 64 machine, and 32-bit on a 32 one.
12.1.2 float
- f32
- f64
12.1.3 boolean
- bool: have value true and false
12.1.4 character
- char
12.2 Compound type
12.2.1 tuple
let tup: (i32, f64, u8) = (500, 6.4, 1); let (x,y,z) = tup;
12.2.2 array
let arr = [1,2,3,4,5]; let first = a[0]; let second = a[1];
12.3 String
The String::new is a static method of the type String.
let mut str = String::new(); // the expect is a method of Result object io::stdin().read_line(&mut str).expect("Failed to read line"); // convert string to num let num: u32 = str.trim().parse().expect("Failed to convert to number"); // convert string to num without crash on exception let num: u32 = match str.trim().parse() { Ok(num) => num, Err(_) => println!("Error but not going to crash"), }
12.4 Result
Result is a type. It can be either Ok or Err. It has expect method, which consume the result, and crash the program if the result is Err. If not want to crash, use match and catch both Ok and Err.
13 Ownership
- each value in Rust has a variable as its owner
- There can only be one owner at a time
- When the owner goes out of scope, the value will be dropped
The ownership changes at assignment. A variable is allocated on stack if the size of it is known, such as the primitive types and tuples containing only primitive types. More compliacated types such as string are allocated on heap.
When doing assignment, the stack variable will do copy, but heap ones will do move. After move, it is not valid to use the previous variable, thus prevent double free. E.g.
let x = 5; let y = x; // now x and y will both be valid, holding a seperate copy of value 5 // on stack let s1 = String::from("hello"); let s2 = s1; // now s1 is invalid to use because it is moved let s3 = String::from("hello"); let s4 = s3.clone(); // now s3 is still valid
The function parameter passing and return value follow the same rule.
13.1 Reference
To use reference in function passing, you have to use reference sign at both end:
fn foo(x: &String) {} fn main() { let x = 8; foo(&x); }
The reference is immutable by default, so to create mutable one:
fn foo(x: &mut String) {} fn main() { let mut x = 8; foo(&mut x); }
Reference can also be used without function
let mut s = String::from("hello"); let r1 = &s; let r2 = &s;
You cannot have multiple mutable reference, or one mutable reference and one or more immutable one.
let mut s = String::from("hello"); let r1 = &mut s; let r2 = &mut s; // error let mut s = String::from("hello"); let r1 = &s; let r2 = &mut s; // error
This prevent dangling reference, and data race. The compiler will ensure the data will not be dropped before the reference goes out of scope.
13.2 Slice
Slice is a reference of part of a collection. It can be a string, or an array, etc.
The string slice type is &str
, it is always immutable.
String literal is actually string slice, thus it is immutable.
let s = String::from("hello world"); let hello = &s[0..5]; // same as let hello = &s[..5]; let world = &s[6..11]; // same as let world = &s[6..s.len()] // same as let world = &s[6..]; // whole let whole = &s[..];
It is similar for an array, the slice has type &[i32]
:
let a = [1,2,3,4,5] let slice = &a[1..3]
Use slice reference to get the first word of a string:
fn first_world(s: &String) -> &str { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return &s[..i]; } } &s[..] }
The compiler will make sure the reference will be valid all the time.
14 control flow
14.1 match
use std::cmp::Ordering; fn main() { let num1 = 1; let num2 = 2; match num1.cmp(&num2) { Ordering::Less => println!("Too small"); Ordering::Greater => println!("Too Big"); Ordering::Equal => { println!("Correct"); println!("Exiting"); } } }
14.2 loop
- loop {}: infinite loop
- can use break and continue
14.3 while
while expr {}
14.4 for
It is used to loop all items in a collection.
let a = [1,2,3,4,5]; for elem in a.iter() {} // reverse for number in (1..4).rev() {}
14.5 if
if expr {} else if expr {} else {}
expr does not need ()
15 Standard Library
- println!
println!("This is x: {} and y: {}", x, y);