Theory and Design of PL (CS 538)
March 30, 2020
This assignment is big, with lots of compiler errors.
&mut self
is ref. to Option<T>
None
to self
take
:
Some(...)
Some(...)
, someone else owns None
.std::mem::replace
, std::mem::swap
let my_str = String::from("Hello world!");
let maybe_str = Some(my_str);
match maybe_str {
None => println!("Nothing!"),
Some(s) => println!("Something!"), // String *moved* into s
// s dropped here
}
println!("Still there? {}", maybe_str.is_none()); // Not OK!
maybe_str
is dropped: inner s
is gone!take
the maybe_str
instead?let mut maybe_str = Some(String::from("Hello world!"));
let mut_str_ref = &mut maybe_str; // type: &mut Option<String>
let took_str = mut_str_ref.take(); // type: Option<String>
// maybe_str is now None
match took_str {
None => println!("Nothing here!"),
Some(s) => ... s owns String ...,
}
println!("Still there? {}", maybe_str.is_none()); // Now OK
[a]
, Maybe a
, …Option<T>
, …foo<T>(t: T)
to foo_i32(t: i32)
One or the other: not both!
fn compute(input: &u32, output: &mut u32) {
if *input > 10 { *output = 1; } // lookup input
if *input > 5 { *output *= 2; } // lookup again
}
fn compute_opt(input: &u32, output: &mut u32) {
let cached_input = *input; // cache *input
if cached_input > 10 {
*output = 2;
} else if cached_input > 5 {
*output *= 2;
}
}
High-level: how Rust analyzes aliasing
'static
is global scope (biggest)'a
refer to some scope
'static
'b:'a
for 'b
contains 'a
'b
lives longer than 'a
'static:'a
, global scope is longest{ // < 'a1
let foo = 1; // |
{ // < 'a2 |
let bar = 2; // | |
{ // < 'a3 | |
let baz = 3; // | | |
} // < | |
} // < |
} // <
'a1:'a2
and 'a2:'a3
static NAME: &'static str = "Steve";
// Omitting lifetimes
fn foo (arg: &String) -> &String { NAME }
// Annotating lifetimes
fn annot_foo<'a> (arg: &'a String) -> &'static String { NAME }
// Return ref doesn't depend on input, lives forever
'a
// Omitting lifetimes
fn plus_foo (arg: &mut String) -> &mut String {
arg.push_str(" and foo");
arg
}
// Annotating lifetimes
fn annot_plus_foo<'a> (arg: &'a mut String) -> &'a mut String {
arg.push_str(" and foo");
arg
}
arg
fn bad_foo () -> &String {
let too_short = String::from("too short");
&too_short
} // too_short goes out of scope, is dropped here
too_short
is dropped
fn bad_foo<'a> () -> &'a String {
let too_short = String::from("too short");
&too_short
} // too_short goes out of scope, is dropped here
'a
for all possible lifetimes 'a
'static
lifetimefn longest<'a> (x: &'a String, y: &'a String) -> &'a String {
if x.len() > y.len() {
x
} else {
y
}
}
x
and y
live at least as long as 'a
, then returned string also lives at least as long as 'a
Self
(in caps) is the type with this trait+
/-
/*
): Add/Sub/Mult// Sized trait: T has size known at compile time
// negative annotation `?Sized`: T *doesn't* need to be Sized
fn foo<T: ?Sized>(t: &T) { ... }