145 lines
5.2 KiB
Rust
145 lines
5.2 KiB
Rust
fn main() {
|
|
// These lines wont work bc m1 and m2 are moved
|
|
// let m1 = String::from("Hello");
|
|
// let m2 = String::from("World");
|
|
// greet(m1, m2);
|
|
// let s = format!("{} {}", m1, m2);
|
|
|
|
// This is suboptimal due to verboseness of solution
|
|
let m1 = String::from("Hello");
|
|
let m2 = String::from("World");
|
|
let (m1_again, m2_again) = greet_ret(m1, m2);
|
|
let s = format!("{} {}", m1_again, m2_again);
|
|
println!("{s}");
|
|
|
|
// references make a convenient usage of ownership
|
|
// Can now use mm1 and mm2 later after the call to greet_ref
|
|
// References are non-owning pointers.
|
|
let mm1 = String::from("Hello");
|
|
let mm2 = String::from("World");
|
|
greet_ref(&mm1, &mm2);
|
|
let ss = format!("{} {}", mm1, mm2);
|
|
println!("{ss}");
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Dereferencing a pointer accesses it data
|
|
|
|
// Rust often implicitly inserts references and dereferences throughout
|
|
// the code; ergo, won't see * often in Rust code
|
|
// This can be seen in str::len
|
|
|
|
let mut x: Box<i32> = Box::new(1);
|
|
let a: i32 = *x; // reads heap value, so a stores 1 not following x
|
|
*x += 1; // *x points to value on heap, now adds 1 to x
|
|
println!("{}", *x);
|
|
|
|
let r1: &Box<i32> = &x; // r1 points to x on the stack
|
|
let b: i32 = **r1; // two dereferences get the heap value
|
|
println!("{}", b);
|
|
|
|
let r2: &i32 = &*x; // r2 points to heap value directly. Making a new reference to the deferenced i32
|
|
let c: i32 = *r2; // one dereference needed to read it
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Rust avoids simultaneous aliasing and mutation
|
|
//
|
|
// Aliasing: accessing same data through different variables
|
|
// aliasing on its own okay. Combining with mutation allows "rug pulling" from other variables
|
|
|
|
// Invalid code:
|
|
// let mut v: Vec<i32> = vec![1, 2, 3];
|
|
// let num: &i32 = &v[2];
|
|
// v.push(4);
|
|
// println!("3rd element is {}", *num); // undefined behavior here
|
|
|
|
// Data in Rust should never be aliased and mutated at the same time
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// References Change Permissions on Places
|
|
//
|
|
// Read (R): Data can be copied to another location
|
|
// Write (W): Data can be mutated
|
|
// Own (O): Dta can be moved or dropped
|
|
// These permissions only exist in the compiler, not at runtime.
|
|
// Default permission are RO, mut makes W
|
|
// References can temporarily remove these permissions.
|
|
// Permissions are defined on places and not just variables
|
|
// A place being anything to the left of the = sign
|
|
// Variables like a, Dereferences like *a, array accesses like a[0]
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Borrow checker finds permissions violations
|
|
|
|
// Anytime a place is used, Rust expects that place to have certain
|
|
// permissions depending on the operation
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// mutable references provide unique and non-owning access to data
|
|
|
|
let mut v: Vec<i32> = vec![1, 2, 3];
|
|
let num: &mut i32 = &mut v[2];
|
|
*num += 1;
|
|
println!("Third element is {}", *num); // actually changes the 2nd element
|
|
println!("Vector is now {:?}", v);
|
|
|
|
// temporarily "downgrade" to a read-only reference
|
|
let mut v: Vec<i32> = vec![1, 2, 3];
|
|
let num: &mut i32 = &mut v[2];
|
|
let num2: &i32 = &*num;
|
|
println!("{} {}", *num, *num2);
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Permissions are returned at the end of a references lifetime
|
|
|
|
let mut x = 1;
|
|
let y = &x;
|
|
let z = *y;
|
|
x += z;
|
|
println!("final x val: {x}");
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Data must outlive all of its references
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Summary
|
|
|
|
// References provide ability to read/write data with consuming ownership.
|
|
// References created with borrows (& and &mut) used dereferenced with * (often implictly)
|
|
|
|
// Off to fixing ownership issues in ch4.3
|
|
}
|
|
|
|
fn greet(g1: String, g2: String) {
|
|
println!("{} {}!", g1, g2);
|
|
}
|
|
|
|
fn greet_ref(g1: &String, g2: &String) {
|
|
println!("{} {}!", g1, g2);
|
|
}
|
|
|
|
fn greet_ret(g1: String, g2: String) -> (String, String) {
|
|
println!("{} {}!", g1, g2);
|
|
(g1, g2)
|
|
}
|
|
|
|
fn ascii_capitalize(v: &mut Vec<char>) {
|
|
let c = &v[0]; // *v has permissions R, W taken away
|
|
if c.is_ascii_lowercase() {
|
|
let up = c.to_ascii_uppercase(); // *v has permissions R, W is given
|
|
v[0] = up;
|
|
} else {
|
|
// *v has R, W is given
|
|
println!("Already Capitalized {:?}", v);
|
|
}
|
|
}
|
|
|
|
fn first(strings: &Vec<String>) -> &String {
|
|
// This function introduces the flow permission, F.
|
|
// F doesn't change throughout the body of a function
|
|
let s_ref = &strings[0];
|
|
s_ref
|
|
}
|
|
|
|
// Doesn't know whether &String is a reference to either strings or default
|
|
// fn first_or(strings: &Vec<String>, default: &String) -> &String {}
|