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

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, into target/debug folder
  • cargo run: to compile and run
  • cargo build --release will enable optimization and output to target/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);