References and Lifetimes
Refs, Ownership, Lifetimes
Can take a reference to an owned thing
let y = 5; let ry = &y;
Now you have an obligation:
ry
must not outlivey
C/C++ will happily let you write things like
int y = 5; int *ry = &y; return ry;
even though the returned pointer is now pointing into whereever on the stack the now-lost
y
wasRust compiler tracks this for you, so that e.g.
let y = 5; let ry = &y; return ry;
will give a compile-time error
Mutable Refs
Refs come in two varieties: "mutable" and "immutable" (really "exclusive" and "shared")
Can only take mutable ref to mutable thing
let mut y = 5; let ry = &mut y;
Once you take a mutable ref, you've essentially "borrowed" the value referred to
- Must not go out of scope
- Cannot take any more references while it is live
- Owner can't do anything with the value while it is live (read it, change it, move it, drop it)
Immutable Refs
An immutable ref is essentially "shared". You can take lots of them if you want
let y = 5; let ry1 = &y; let ry2 = &y;
You are still restricted for safety
- Owner cannot drop or move value (nor mutate, duh) while refs are live
More About Refs
A lot of automatic derefing happens
- With the "." structure / enum operator
- With comparison operators
etc
let y = 10; let ry = &y; let rry = &ry; assert!(9 < rry);
There's no such thing as a "null reference" (in safe code): no way to produce one, no need to guard against them
If you need a "nullable" value, use the
Option
typelet y = 10; let ory = Some(&y);
This is true for refs or anything else
You can get a reference to an anonymous variable implicitly defined by an expression
let ry = &10;
Explicit Lifetimes
The machinery that the compiler uses to check lifetimes is by default "under the hood": does the checking for you without intervention
Sometimes, though, you need (or want) to get explicit access to that machinery to allow a program to compile that is safe but won't by default
"Named lifetimes" start with a tick, e.g.
'a
("tick-a")In many contexts, explicit lifetime names can be declared
fn f<'a, 'b>(x: &u64, y: &u64) -> &u64
These names can then be used to describe lifetime constraints for referenced data
fn f<'a, 'b>(x: &'a u64, y: &'b u64) -> &'a u64
In this example, the lifetime of the returned data must the same as the lifetime of x's data
fn f<'a, 'b: 'a>(x: &'a u64, y: &'b u64) -> &'a u64
We can also require that result's data live at least as long as y's
fn f<'a, 'b: 'a>(x: &'a u64, y: &'b u64) -> &'b u64
https://play.rust-lang.org/?gist=59636f6153699652df21d05ea61f3428&version=stable
Book Example
Rust Parametric Types
Actually "monomorphic" or "template" types, but…
Just like we can provide lifetime variables, can provide type variables to get "generic" thingies
fn id<T>(x: T) -> T { x }
Can now call with whatever type as long as they match
assert_eq!(id(5u32), 5u32); assert_eq!(id("hello"), "hello");