Learned from official book. One simple advice, Check the source code, there are a lot of usage examples and explanation.
Key Concepts
- Module & Crate
- Ownership: clone | move
- Borrow (reference) & lifecycle (
'static
, 'a
)
- Struct, trait, impl for, as,
dyn Any
- Macro (meta programming)
- Closure
- Smart pointers
Module & Crate
package (cargo project) -> crate (module) -> your code.
1
2
3
4
5
6
7
|
rustlings // project
├── src // source dir
├── library // mod or crate
│ ├── book.rs
│ └── user.rs
├── main.rs // main
└── lib.rs // library (declaration and use)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
pub struct Button;
pub mod a {
// use super to call parent level mod
use super::Button;
pub struct AdButton {
pub b: Button,
}
}
pub mod b {
// use crate to call same level mod
use crate::a::AdButton;
// use self to call current mod
use self::c::CdButton;
fn bd() {
let _a = AdButton { b: Button };
let _c = CdButton;
}
pub mod c {
pub struct CdButton;
}
}
|
Ownership
- For passing a clone, the callee shall impl trait
Copy
- Ints, bool, floats,
&str
, tuple are compatible with Copy
trait
Copy
is shallow clone, Clone
is deep clone
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
fn takes_ownership(s: String) {
// do something
}
fn gives_ownership() -> String {
let a = String::from("hello");
a
}
fn main() {
let s = String::from("ok");
takes_ownership(s);
// if we call println! after takes_ownership, we would encounter a compile error
println!("{}", s);
let s1 = gives_ownership();
println!("it's ok to println! s1: {}", s1);
}
|
Borrow or Move
Only one mutable borrow or multiple immutable borrows at one time.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
mod bm {
fn borrow(s: &String) {
println!("{}", s);
}
fn borrow_mut(s: &mut String) {
s.push_str(" zzz");
println!("{}", s)
}
fn main() {
let s = String::from("123");
borrow(&s);
borrow(&s);
let mut z = String::from("zzz");
borrow_mut(&mut z);
// compile error under below, extra immutable borrow
borrow(&z);
}
}
|
Struct & Trait
'static
lives with the application
'a
indicate a specific scope
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
mod st {
trait Printer {
fn print(&self);
}
struct Console;
impl Printer for Console {
fn print(&self) {
println!("I am a console")
}
}
fn call_printer(p: &'static dyn Printer) {
p.print()
}
// a & b are in same scope or a's scope include b's
fn lifecycle<'a>(a: &'a str, b: &'a str) {
println!("{}{}", a, b)
}
}
|
Macro
meta programming in rust, I didn’t know much yet, here is an example.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
macro_rules! success {
($fmt:literal, $ex:expr) => {{
use console::{style, Emoji};
use std::env;
let formatstr = format!($fmt, $ex);
if env::var("NO_EMOJI").is_ok() {
println!("{} {}", style("✓").green(), style(formatstr).green());
} else {
println!(
"{} {}",
style(Emoji("✅", "✓")).green(),
style(formatstr).green()
);
}
}};
}
|
Closure
Fn
& FnMut
& FnOnce
as Function pointers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
mod cl {
use std::thread;
fn a() {
let _print = |x: Box<dyn Display>| {
println!("{}", x);
};
let _print_move = move |x: String| {
println!(x);
};
let s = String::from("oops");
thread::spawn(move || {
println!(s);
});
let _pp = |f: dyn Fn<()>| {
f();
};
}
fn do_twice<F>(mut func: F)
where F: FnMut<()> {
func();
}
}
|
Smart Pointers
trait Deref
& Drop
for dereference (*a
) and graceful recycle
Box<T>
for allocating values on the heap
Rc<T>
, a reference counting type that enables multiple ownership
Ref<T>
and RefMut<T>
, accessed through RefCell<T>
, a type that enforces the borrowing rules at runtime instead of compile time
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
mod sp {
use std::cell::RefCell;
use std::rc::{Rc, Weak};
fn t_box() {
// move value from stack to heap
let val: u8 = 5;
let _b: Box<u8> = Box::new(val);
// move value from heap to stack by dereference
let boxed: Box<u8> = Box::new(5);
let _val: u8 = *boxed;
}
struct Owner {
name: String,
gadgets: RefCell<Vec<Weak<Gadget>>>,
}
struct Gadget {
id: i32,
owner: Rc<Owner>,
}
fn t_rc() {
let gadget_owner: Rc<Owner> = Rc::new(
Owner {
name: "Gadget Man".to_string(),
gadgets: RefCell::new(vec![]),
}
);
let g1 = Rc::new(
Gadget {
id: 1,
owner: Rc::clone(&gadget_owner),
}
);
let g2 = Rc::new(
Gadget {
id: 2,
owner: gadget_owner.clone(),
}
);
{
let mut gadgets = gadget_owner.gadgets.borrow_mut();
gadgets.push(Rc::downgrade(&g1));
gadgets.push(Rc::downgrade(&g2));
}
for gadget_weak in gadget_owner.gadgets.borrow().iter() {
let g = gadget_weak.upgrade().unwrap();
println!("Gadget {} owned by {}", g.id, g.owner.name);
}
}
}
|
At Last
Hopefully, there are many rust projects on github, I shall learn them along with practices.