Structs
Rust struct
Seen a bunch of these already
Basic idea is the same as structures in any language
Rust structs are fancy, and an essential building block
Most of the standard API is built out of struct, enum, trait
Basics
Basic decl syntax is straightforward
struct Point { x: i64, y: i64, }
There is no default constructor: can create with again-obvious syntax
let p = Point { x: 5, y: -2 };
Fancy Features
Common case of tag-name = varname is special
let x = 5; let p = Point { x, y: -2 };
Can have empty structures: these are inhabited by only one value
struct Empty; let e = Empty;
"Tuple structs" have anonymous fields
struct Point(i64, i64); let p = Point(3, -2); let x = p.0;
Single-element tuple structs are often used for "newtyping"
struct Temperature(i64);
Ownership and Lifetimes
Ownership is a bit complicated with structs
A struct value owns all of its fields individually
struct TwoString { s1: String, s2: String, }
Moving a field out of a struct makes it "partially moved"
let t = TwoString { s1: "hello".to_string(), s2: "world".to_string(), }; let s1 = t.s1; println!("{}", s1); // prints "hello" // println!("{}", t.s1); // compiler error // return t; // compiler error: t partially moved
References stored in structs need to outlast the struct
struct TwoStr<'a, 'b> { s1: &'a str, s2: &'b str, } let s = "hello".to_string(); let t = TwoStr { s1: &s, s2: "world" }; drop(s); println!("{}", t.s1); // prints "hello"
Normally need to explicitly specify reference lifetime
impl
Mechanism for getting that fancy OO-style syntax
Implementor defines whether self argument is by move, reference or mut reference (
examples/point.rs
)Allows chaining of operators, which can be syntactically nice
Deriving
Mechanism for getting traits defined automatically for your struct
Uses attribute syntax
Common kind of thing to write (
examples/units.rs
)#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] struct Temperature(i64);
Pattern Matching
Not specific to structs / enums
Mechanism for deconstructing a composite thing
let (x, h) = (3, "hello"); println!("{} {}", x, h); let &[v1, v2, v3] = &[4, 5, 6]; println!("{}", v1 + v2 + v3);
We've seen some of this before.
Two kinds: "refutable" and "irrefutable"
let
requires irrefutable patternmatch
allows refutable pattern, but all possible patterns must be matched
Match
The
match
statement is the canonical pattern matching toolstruct Point { x: i64, y: i64, } let p = Point{x: 3, y: 5}; let z = match p { Point{x: 3, y: 5} => { println!("is good"); 8 } _ => { panic!("bad point"); } }; match p { Point{x: xx@1...3, ..} => println!("{}", xx), Point{x: xx@0, y: 4} => println!("{}", xx), _ => panic!("bad point"), }
Note the existence of
..
,...
,|
(not shown, only at top level),@
First match is chosen
Compiler checks that every possible value will be matched, and that later matches are not subsumed by earlier
Pattern Variable Modifiers
Rules for move/borrow are the same for pattern variables
For convenience, you can modify a pattern variable with
ref
orref mut
to have it borrowed instead of moved
Misc
Struct layout concepts are in the book: not too important
Need to master structs: they are used everywhere