sharils.github.io/slides/20220731-learn-rust-verbs
app.sli.do/event/pdmHAZWcGDERsxZzcx6i7g
fn main() {
let bad_beef = 0xBadBeef;
println!("No {:#x}", bad_beef);
}
Easy.
fn inspect(bad_beef: i32) -> i32 {
dbg!(bad_beef);
return bad_beef;
}
fn main() {
let bad_beef = 0xBadBeef;
println!("No {:#x}", inspect(bad_beef));
}
Let's inspect.
fn inspect(bad_beef: String) -> String {
dbg!(bad_beef);
return bad_beef;
}
fn main() {
let bad_beef = String::from("Bad Beef!");
println!("No {}", inspect(bad_beef));
}
Now a String.
Compiling playground v0.0.1 (/playground)
error[E0382]: use of moved value: `bad_beef`
--> src/main.rs:3:12
|
1 | fn inspect(bad_beef: String) -> String {
| -------- move occurs because `bad_beef` has
type `String`, which does not
implement the `Copy` trait
2 | dbg!(bad_beef);
| -------------- value moved here
3 | return bad_beef;
| ^^^^^^^^ value used here after move
For more information about this error, try `rustc --explain E0382`.
error: could not compile `playground` due to previous error
Oops!
// 3. Copy i32 to bad_beef
fn inspect(bad_beef: i32) -> i32 {
dbg!(bad_beef); // 4. Copy bad_beef to dbg ✅
return bad_beef; // 5. Copy bad_beef to main
}
fn main() {
// 1. Copy i32 to bad_beef
let bad_beef = 0xBadBeef;
// 2. Copy bad_beef to inspect
// 6. Copy bad_beef to println
println!("No {:#x}", inspect(bad_beef));
}
Stack | Heap | |
---|---|---|
Access | Faster | Slower |
Allocation | Faster | Slower |
Drop | Faster | Slower |
Jump | Less | More |
Size | Known&Fixed | Unknown∥Dynamic |
copy
→ impl Copy
→ Stack-Only Data
→ Known&Fixed Size At Compile Time
fn inspect(bad_beef: String) -> String {
dbg!(bad_beef);
return bad_beef;
}
fn main() {
let bad_beef = String::from("Bad Beef!");
println!("No {}", inspect(bad_beef));
}
Not too early, not too late, not too many, not too few and no GC, i.e.
at the right time, for the right times.
// 4. Move String into bad_beef
fn inspect(bad_beef: String) -> String {
dbg!(bad_beef); // 5. Move bad_beef into dbg
// 6. Invalidate bad_beef in inspect
// 7. Drop bad_beef at the end of dbg
return bad_beef; // 8. Move bad_beef into main 💥
}
fn main() {
// 1. Move String into bad_beef
let bad_beef = String::from("Bad Beef!");
// 2. Move bad_beef into inspect
// 3. Invalidate bad_beef in main
// 9. Move String into println
println!("No {}", inspect(bad_beef));
}
Move: change of ownership i.e. right to drop.
─┬─ stack ─┬─ Copy ─── copy ─── Duration
│ │
│ ├─ Drop ─── move ─── ?
│ │
│ └─ None ─── move ─── ?
│
└─ heap ──┬─ Drop ─── move ─── Vec
│
└─ None ─── move ─── String
Compiling playground v0.0.1 (/playground)
error[E0382]: use of moved value: `bad_beef`
--> src/main.rs:3:12
|
1 | fn inspect(bad_beef: String) -> String {
| -------- move occurs because `bad_beef` has
type `String`, which does not
implement the `Copy` trait
2 | dbg!(bad_beef);
| -------------- value moved here
3 | return bad_beef;
| ^^^^^^^^ value used here after move
For more information about this error, try `rustc --explain E0382`.
error: could not compile `playground` due to previous error
How about deep copy?
fn inspect(bad_beef: String) -> String {
// 1. Clone bad_beef
// 2. Move clone of bad_beef into dbg ✅
dbg!(bad_beef.clone());
return bad_beef;
}
fn main() {
let bad_beef = String::from("Bad Beef!");
println!("No {}", inspect(bad_beef));
}
// 3. Move String into bad_beef
fn inspect(bad_beef: String) -> String {
dbg!(&bad_beef); // 4. Borrow bad_beef
// 5. Copy borrow of bad_beef to dbg ✅
return bad_beef; // 6. Move bad_beef into main
}
fn main() {
// 1. Move String into bad_beef
let bad_beef = String::from("Bad Beef!");
// 2. Move bad_beef into inspect
// 7. Move String into println
println!("No {}", inspect(bad_beef));
}
Borrow: no change of ownership → no right to drop.
// 4. Copy borrow of String to bad_beef
fn inspect(bad_beef: &String) -> &String {
dbg!(bad_beef); // 5. Copy borrow of bad_beef to dbg
return bad_beef; // 6. Copy borrow of bad_beef to main
}
fn main() {
// 1. Move String into bad_beef
let bad_beef = String::from("Bad Beef!");
// 2. Borrow bad_beef
// 3. Copy borrow of bad_beef to inspect ✅
// 7. Copy borrow of String to println
println!("No {}", inspect(&bad_beef));
}
Borrow before inspect.
// 4. Copy borrow of String to bad_beef
fn inspect(bad_beef: &String) -> &String {
dbg!(bad_beef); // 5. Copy borrow of String to dbg
return bad_beef; // 6. Copy borrow of String to main
}
fn main() {
// 1. Borrow String
// 2. Copy borrow of String to bad_beef ✅
let bad_beef = &String::from("Bad Beef!");
// 3. Copy borrow of String to inspect
// 7. Copy borrow of String to println
println!("No {}", inspect(bad_beef));
}
Borrow on initialisation.
// 3. Copy borrow of String to bad_beef
fn inspect(bad_beef: &String) -> &String {
dbg!(bad_beef); // 4. Copy borrow of String to dbg
return bad_beef; // 5. Copy borrow of String to main
}
fn main() {
// 1. Move String into bad_beef
let bad_beef = String::from("Bad Beef!");
// 2. Move String into inspect 💥
// 6. Copy borrow of String to println
println!("No {}", inspect(bad_beef));
}
String ≠ &String
fn inspect(bad_beef: String) -> String {
// pub fn clone(&self) -> String ⁉️
dbg!(bad_beef.clone());
return bad_beef;
}
fn main() {
let bad_beef = String::from("Bad Beef!");
println!("No {}", inspect(bad_beef));
}
automatic referencing and dereferencing!
fn inspect(bad_beef: &str) -> &str {
dbg!(bad_beef);
return bad_beef;
}
fn main() {
// string literal is a string slice
let bad_beef = "Bad Beef!";
println!("No {}", inspect(bad_beef));
}
String literal
fn inspect(bad_beef: &str) -> &str {
dbg!(bad_beef);
return bad_beef;
}
fn main() {
// a string slice of String
let bad_beef = &String::from("Bad Beef!")[..];
println!("No {}", inspect(bad_beef));
}
String slice
ref ─┬─ slice ─┬─ String slice ─── string literal
│ │
│ ├─ Vec slice
│ │
│ └─ ...
│
└─ borrow
fn emphasise(bad_beef: &mut String) {
*bad_beef += "?"; // 1. Deref *bad_beef to mut String
// 2. Call String’s add_assign
}
fn main() {
let mut bad_beef = String::from("Bad Beef!");
emphasise(&mut bad_beef);
println!("No {}", bad_beef);
}
Deref
fn inspect(bad_beef: &str) -> &str {
dbg!(bad_beef);
return bad_beef;
}
fn main() {
let bad_beef = String::from("Bad Beef!");
// 1. borrow bad_beef as &String
// 2. deref coerce &String to &str
// 3. Copy &str to inspect
println!("No {}", inspect(&bad_beef));
}
Deref coercion
use std::cell::RefCell;
fn emphasise(bad_beef: &RefCell<String>) {
*bad_beef.borrow_mut() += "?";
}
fn main() {
let bad_beef = RefCell::new(String::from("Bad Beef!"));
emphasise(&bad_beef);
println!("No {}", bad_beef.borrow() );
}
&mut | RefCell | |
---|---|---|
Borrow Checker | Compile Time | Runtime |
Catch Error | Compiler | Code Review |
Runtime Cost | Zero | More |
Violation | Won't compile | Runtime Panic |
Safe Case Coverage | Less | More |
use std::rc::Rc;
fn inspect(bad_beef: Rc<String>) -> Rc<String> {
// 1. Clone Rc<String> (shallow copy)
// 2. Increase strong count
// 3. Move the clone of Rc into dbg
// 4. Drop the clone of Rc at the end of dbg
// 5. Decrease strong count
dbg!(Rc::clone(&bad_beef));
return bad_beef;
}
fn main() {
let bad_beef = Rc::new(String::from("Bad Beef!"));
println!("No {}", inspect(bad_beef));
}
No Rc | Use Rc | |
---|---|---|
Catch Error | Compiler | Code Review |
Runtime Cost | Zero | More |
Violation | Won't Compile | Memory Leak |
Circular Reference | Improbable | Possible |
Use Weak (as in weak reference) to fix circular reference memory leak.
Try to say those verbs (copy, move, clone, borrow, ref, deref) when you are typing =
(copy/move), &
(borrow/ref), *
(deref)
app.sli.do/event/pdmHAZWcGDERsxZzcx6i7g
If you are looking for a full time and fully remote 10+ years web developer (and 3+ years of team lead) with production experience in AWS, MongoDB, MySQL, Elixir, Phoenix, JavaScript, TypeScript, React, React Native, NativeBase, OpenAPI, Apollo GraphQL, Docker, Shell.
Please write to [email protected]