Shared behavior
A trait names shared behavior
A trait is a set of methods a type can promise. impl attaches that promise to one type. The call pier.meters() works because Pier implements Meters.
trait Meters {
fn meters(&self) -> u32;
}
struct Pier {
span: u32,
}
impl Meters for Pier {
fn meters(&self) -> u32 {
self.span
}
}
fn main() {
let pier = Pier { span: 12 };
println!("{}", pier.meters());
}
What to notice. The trait says what a type can do. The impl says how Pier does it. Another struct can implement Meters with a different body.
Try itA generic function keeps the type open
wider is written once and used for any type that can be ordered. T stands for that type. PartialOrd is the bound that allows the comparison.
fn wider<T: PartialOrd>(left: T, right: T) -> T {
if left > right { left } else { right }
}
fn main() {
println!("{}", wider(4, 12));
}
What to notice. Both arguments share one T, so they must be the same type. The bound is what makes > legal. i32 implements PartialOrd, which is why 4 and 12 are allowed.
Try itA lifetime ties a borrow to its source
choose returns one of the two borrows. The name 'a tells the compiler that the returned reference must not outlive either input. Both strings are still alive when name is printed.
fn choose<'a>(left: &'a str, right: &'a str, take_left: bool) -> &'a str {
if take_left { left } else { right }
}
fn main() {
let north = String::from("north");
let south = String::from("south");
let name = choose(&north, &south, true);
println!("{name}");
}
What to notice. 'a is not a value at runtime. It is a promise about how long the borrow lasts. One lifetime on both inputs means the return is allowed to be either of them.
Try itA module hides what it does not publish
mod pier nests a scope inside this file. Items are private unless they are pub. main can call label because the function is public. The path is pier::label.
mod pier {
pub fn label() -> &'static str {
"north"
}
}
fn main() {
println!("{}", pier::label());
}
What to notice. A module can live in this file or in its own file. Privacy is the same either way: the parent can see pub items, and private items stay inside the module.
Try itLists and closures
An iterator walks a list without a loop index
iter lends each item. copied turns &i32 into i32, which is allowed because integers are Copy. filter keeps values greater than 5, and collect builds a new Vec.
fn main() {
let spans = [3, 12, 7];
let long: Vec<i32> = spans.iter().copied().filter(|meters| *meters > 5).collect();
println!("{}", long.len());
}
What to notice. The array itself is unchanged. filter receives a reference to each item, so the closure writes *meters. The type on long is what tells collect which collection to build.
Try itA closure can use the names around it
The vertical bars name the closure's arguments. extra is not an argument. The closure captures it from main and adds it when coat is called.
fn main() {
let extra = 2;
let coat = |meters: i32| meters + extra;
println!("{}", coat(10));
}
What to notice. A closure is a value. This one borrows extra. It can be called like a function, and it still sees the binding it captured.
Try itdyn keeps the trait and forgets the concrete type
Pier and Cable are different structs. Both implement Label. Box<dyn Label> lets one vector hold either, because the box stores a pointer to the value and to the trait's methods.
trait Label {
fn name(&self) -> &'static str;
}
struct Pier;
struct Cable;
impl Label for Pier {
fn name(&self) -> &'static str {
"pier"
}
}
impl Label for Cable {
fn name(&self) -> &'static str {
"cable"
}
}
fn main() {
let parts: Vec<Box<dyn Label>> = vec![Box::new(Pier), Box::new(Cable)];
println!("{}", parts.len());
}
What to notice. dyn Label has no fixed size, so it cannot sit in the vector by itself. The box has a known size. The concrete type is chosen when Box::new runs.
Try itConcurrency
join waits for a thread
spawn starts a closure on another thread. The closure returns 12. join waits until that thread finishes and hands the 12 back to main.
use std::thread;
fn main() {
let handle = thread::spawn(|| 12);
let meters = handle.join().unwrap();
println!("{meters}");
}
What to notice. join returns a Result. unwrap takes the Ok value. The closure captures nothing, so it is safe to send to the new thread.
Try itA channel moves a value across threads
mpsc::channel builds a sender and a receiver. The new thread owns the sender and sends 12. main receives that message and prints it. send moves the value. It does not share it.
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
tx.send(12).unwrap();
});
let meters = rx.recv().unwrap();
println!("{meters}");
}
What to notice. mpsc means many senders, one receiver. recv waits until a message arrives. The move on the closure is required because tx is used on the other thread and must not stay in main.
Try itArc and Mutex share one count
Three threads each add one to the same counter. Arc lets them share ownership of the counter. Mutex lets only one of them change it at a time. After every join, the count is 3.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let bolts = Arc::new(Mutex::new(0));
let mut handles = Vec::new();
for _ in 0..3 {
let bolts = Arc::clone(&bolts);
handles.push(thread::spawn(move || {
let mut count = bolts.lock().unwrap();
*count += 1;
}));
}
for handle in handles {
handle.join().unwrap();
}
println!("{}", *bolts.lock().unwrap());
}
What to notice. Arc::clone adds an owner. It does not copy the integer. lock waits for the mutex. The inner bolts binding shadows the outer one and is the owner that moves into that thread.
Try itAn async function builds a future
Calling meters does not run its body and does not produce 12. It returns a future, which is a value that describes the work. This program builds that value, prints a line, and drops the future. Nothing polls it, so the body never runs.
async fn meters() -> u32 {
12
}
fn main() {
let future = meters();
println!("future built");
drop(future);
}
What to notice. await is how async code waits on a future, and it is allowed only inside an async function. main is ordinary, and this program has no executor. An executor is the loop that polls a future until it finishes.
Try it compiles this program. It still does not run the body of meters. Running a future takes an executor, which is a separate crate, and this lesson stays in the standard library.
Try it