labs: basic grammars of Rust

week1: helloworld

cargo

Rust 语言的包管理工具是 cargo

1
2
cargo new world_hello 
cd world_hello

run a program

有两种方式可以运行项目:

  1. cargo run
  2. 手动编译和运行项目

cargo run

编译快,但是运行慢,本质是debug模式!

`cargo run –release’

编译慢运行快, release模式

cargo check

快速检查代码能否编译通过

basic grammar for rust

Numeric types

Numeric types in Rust include i8i16i32, and i64 (all of which store signed – positive or negative – numbers), as well as u8u16u32, and u64 (which store unsigned – strictly nonnegative – numbers).

To declare a variable, we use the let keyword and specify the variable’s type:

1
let n: i32 = 1;

Also, Rust allows you to omit the variable’s type when the compiler is able to figure out what the type should be.

1
let n = 1;

variables in Rust are constants by default. Add mut to make a variable mutable.

1
2
let mut n = 0;
n = n + 1; // compiles fine

strings

There are two string types: &str and String&str is an immutable pointer to a string somewhere in memory. For example:

1
let s: &str = "Hello world";    // the ": &str" annotation is optional

Not allowed to mutate the string &str!
The String type stores a heap-allocated string. You’re allowed to mutate Strings (so long as you use the mut keyword).

1
2
let mut s: String = String::from("Hello "); // "String" type annotation is optional
s.push_str("world!");

The first line allocates memory on the heap; s is a String object containing a pointer to that memory. The second line appends to the heap buffer, reallocating/resizing the buffer if necessary. The memory for the string will be automatically freed after the last line where s is used.

vector

The easiest way to store collections of things is in a vector:

1
2
3
4
5
6
let mut v: Vec<i32> = Vec::new();
v.push(2);
v.push(3);
// Even here, the ": Vec<i32>" type annotation is optional. If you omit it, the
// compiler will look ahead to see how the vector is being used, and from that, it
// can infer the type of the vector elements. Neat!

Rust also supports fixed-size arrays. Unlike C, the length of the array is stored as part of the array type. In addition, array accesses are generally bounds-checked at runtime. No buffer overflows, please!

1
2
3
let mut arr: [i32; 4] = [0, 2, 4, 8];
arr[0] = -2;
println!("{}", arr[0] + arr[1]);

loops

You can iterate over collections using iterators and some very nice, Python-like syntax:

1
2
3
for i in v.iter() { // v is the vector from above
println!("{}", i);
}

While loops:

1
2
3
while i < 20 {
i += 1;
}

Rust also has a special loop that should be used instead of while true:

1
2
3
4
5
let mut i = 0;
loop {
i += 1;
if i == 10 { break; }
}

functions

Rust functions are declared like so:

1
2
3
4
5
6
7
8
9
// Function with return type i32
fn sum(a: i32, b: i32) -> i32 {
a + b
}

// Void function (no "->")
fn main() {
// do stuff...
}
  • Unlike variables (where Rust will happily infer the variable type), you are required to specify the return type for functions that return values.
  • There’s no return keyword in the sum function! And… it’s missing a semicolon!

I/O

output:

1
println!("xxx, {}", the_exp);

expression-based

In most languages that you are probably used to, there are expressions (which evaluate to values) and statements (which do not).
Rust is an expression-based language.