rust Flashcards
what type does the vector have to be to be able to call push on it
mutable
how can you make a copy of the variable n1?
let n4 = n1
what does this do?
let n2: &i32 = &n1;
a &i32 referring to n1
what does this do?
let n4 = n1;
// another i32 variable containing a copy of 1234
what will happen
let mut n1 = 1234_i64;
let n2 = &n1;
n1 = 4567;
compiles
what are three types of borrowing
use the original value;
have several immutable references to it;
have one mutable reference.
what will happen:
let mut n1 = 1234;
let n2 = &mut n1;
*n2 = 4567;
println!(“{}”, *n2);
println!(“{} {}”, n1, n2);
let mut n1 = 1234;
let n2 = &mut n1; // note: a mutable reference
*n2 = 4567;
println!(“{}”, *n2);
//println!(“{} {}”, n1, n2); // “cannot borrow n1
as immutable because it is also borrowed as mutable”
will this compile
let mut n1 = 1234;
{
let n2 = &mut n1;
*n2 = 4567;
println!(“{}”, *n2);
}
n1 = 7890;
println!(“{}”, n1);
let mut n1 = 1234;
{
let n2 = &mut n1;
*n2 = 4567;
println!(“{}”, *n2); // prints 4567
} // n2 is out of scope here, so no longer has a reference
n1 = 7890;
println!(“{}”, n1); // prints 7890
will this compile
let mut n1 = 1234;
let n2 = &mut n1;
let n3 = &mut n1;
yes
what happens when you pass a value to a function
When a value is passed to a function, ownership is given permanently to the function. Or, ownership moves to the function: is transferred permanently.
will this compile
sum_vec(nums: Vec<i64>) -> Vec<64> {}</i64>
let numbers = Vec::from([1, 2, 3, 4]);
println!(“{}”, sum_vec(numbers));
println!(“”, numbers);
let numbers = Vec::from([1, 2, 3, 4]);
println!(“{}”, sum_vec(numbers)); // prints 10
println!(“”, numbers);
borrow of moved value: numbers
how do you ask for references
fn some_func( val : &Vec<i64>) -> i64 { ...}</i64>
will this compile
fn sum_vec(values: &Vec<i64>) -> i64 {
values.iter().fold(0, |a, b| a + b)
}</i64>
let numbers = Vec::from([1, 2, 3, 4]);
println!(“{}”, sum_vec(&numbers));
println!(“”, numbers);
yes
question; what happens if i pass append_sum(mut val); ?
chatgpt it
will this compile:
let mut numbers = Vec::from([1, 2, 3, 4]);
append_sum(&mut numbers);
println!(“”, numbers);
let mut numbers = Vec::from([1, 2, 3, 4]);
append_sum(&mut numbers);
println!(“”, numbers); // prints [1, 2, 3, 4, 10]
what are the two ownership rules
- At any given time, you can have either one mutable reference or any number of immutable references.
- References must always be valid.
whats the function argument type for a mutable reference
&mut
will this work:
fn sum_vec(values: Vec<i64>) -> i64 {
values.iter().fold(0, |a, b| a + b)
}</i64>
fn main() {
let numbers = Vec::from([1, 2, 3, 4]);
let summed = sum_vec(numbers);
println!(“{}”, summed);
println!(“{}”, summed);
}
yes
will this work:
fn sum_vec(values: Vec<i64>) -> i64 {
values.iter().fold(0, |a, b| a + b)
}</i64>
fn main() {
let numbers = Vec::from([1, 2, 3, 4]);
let summed = sum_vec(numbers);
println!(“”, numbers);
}
no, because its given to sum_vec
will this work:
fn sum_vec(values: &Vec<i64>) -> i64 {
values.iter().fold(0, |a, b| a + b)
}
fn main() {
let numbers = Vec::from([1, 2, 3, 4]);
let summed = sum_vec(numbers);
println!("{:?}", numbers);
println!("{}", summed);
}</i64>
no, becuase numbers is not passed as a reference in the main function
will this work:
fn sum_vec(values: &Vec<i64>) -> i64 {
values.iter().fold(0, |a, b| a + b)
}
fn main() {
let numbers = Vec::from([1, 2, 3, 4]);
let summed = sum_vec(&numbers);
println!("{:?}", numbers);
println!("{}", summed);</i64>
yes
will this work:
fn print_int(n: i64) {
println!(“”, n);
}
fn print_vec(v: Vec<i64>) {
println!("{:?}", v);
}</i64>
let n: i64 = 7;
let v: Vec<i64> = Vec::from([1, 2, 3, 4]);
println!("n: {:?}", n);
println!("v: {:?}", v);
print_int(n);
print_vec(v);
println!("n: {:?}", n);
println!("v: {:?}", v);</i64>
only works for print n because Vec does not implement Copy trait
what are some requirements to automatically copy a value as a reference to a function
types that implement the copy trait
what are the types that have their ownership given away when they are assigned to a different variable?
non-copy types
describe these lines of code:
let n1: i64 = 7;
let v1: Vec<i64> = Vec::from([1, 2, 3, 4]);
let n2 = n1;
let v2 = v1;
println!("n1: {:?}", n1);
println!("n2: {:?}", n2);
println!("v1: {:?}", v1);
println!("v2: {:?}", v2);</i64>
let n1: i64 = 7;
let v1: Vec<i64> = Vec::from([1, 2, 3, 4]);
let n2 = n1; // implicitly a copy, because i64 is Copy
let v2 = v1; // *implicitly a move*, because Vec is not Copy
println!("n1: {:?}", n1);
println!("n2: {:?}", n2);
//println!("v1: {:?}", v1); // "borrow of moved value: `v1`"
println!("v2: {:?}", v2);</i64>
how to implement copy
[derive(Copy, Clone)]
Summary:
- derive from Copy, Clone
- or, call clone on the fields
struct MyStruct;
- struct MyStruct {
field1: String,
field2: Vec<u64>,
}</u64>
impl Clone for MyStruct {
fn clone(&self) -> Self {
MyStruct {
field1: self.field1.clone(),
field2: self.field2.clone(),
}
}
}
why is it hard to implement copy trait for a vector?
The Vec keeps most of its data on the heap: copying its stack value would create multiple references to that heap memory.
which is implicit:
cloning or copying
copying is implicit but cloning is not
what does #[derive(Debug, Clone)] do
The #[derive…] line gets us free implementations of Debug (printing with {:?}) and Clone (a .clone() method).
how to initialize a struct so that the struct is modifiable
let mut p = GeoPoint {…}
how to implement the following function in struct
fn antipode(&self) -> GeoPoint {}
impl GeoPoint {
fn antipode(&self) -> GeoPoint {
GeoPoint {
lat: -self.lat,
lon: -self.lon,
ele: self.ele,
}
}
}
what is equivalent to this:
fn antipode(&self) -> GeoPoint {…}
fn antipode(self: &GeoPoint) -> GeoPoint {…}
what are the 3 receiving arguments for a struct. and what are they used for
- the value itself (self): when the struct is no longer needed
- or a reference (&self):
- a mutable reference (&mut self): change a structs own content
what is an associated function
If we implement a function on a type that does not have a receiver argument (self), it is an associated function (≈ static method).
how do you access associated functions
Associated functions are accessed from the type with ::
let p = GeoPoint::new(49.267, -122.967);
how do you simplify this:
impl GeoPoint {
fn new(lat: f64, lon: f64) -> GeoPoint {
GeoPoint {
lat: lat,
lon: lon,
ele: 0,
}
}
}
fn new(lat: f64, lon: f64) -> GeoPoint {
GeoPoint { lat, lon, ele: 0 }
}
how do you create a destructor
by creating a method with self ownership:
fn consume(self) {…}
implement Display trait for GeoPoint, where the trait definition is:
pub trait Display {
// Required method
fn fmt(&self, f: &mut Formatter<’_>) -> Result<(), Error>;
}
and GeoPoint is:
impl GeoPoint {
fn new(lat: f64, lon: f64) -> GeoPoint {
GeoPoint {
lat: lat,
lon: lon,
ele: 0,
}
}
}
Reminders:
- write!(f, “{}, {}, {}”, self.lat, self.lon, self.ele)
use std::fmt;
impl fmt::Display for GeoPoint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, “{}°N {}°E @ {}m”, self.lat, self.lon, self.ele)
}
}
how to implement a default constructor
- # [derive(Default)]
2.
struct Foo {
x: i32,
y: String,
}
impl Foo {
fn new() -> Foo {
Foo {
x: 0,
y: String::from(“default”),
}
}
}
define your own upable trait for geopoint
pub trait Upable {
fn move_up(&mut self, height: i32);
fn copy_up(&self, height: i32) -> Self;
}
impl Upable for GeoPoint {
fn move_up(&mut self, height: i32) {
self.ele += height;
}
fn copy_up(&self, height: i32) -> GeoPoint {
let mut dup = (*self).clone();
dup.move_up(height);
dup
}
}
understand all this:
let v1: Vec<i64> = Vec::new();
let v2 = Vec::<i64>::new();
let v3 = returns_a_vec_i64();
let mut v4 = Vec::new();
v4.push(1234_i64);</i64></i64>
let v1: Vec<i64> = Vec::new(); // explicit type
let v2 = Vec::<i64>::new(); // type parameter in initializer
let v3 = returns_a_vec_i64(); // implied by initializer
let mut v4 = Vec::new(); // type-inferred in next line...
v4.push(1234_i64);</i64></i64>
write your own pari_with_one function that returns a tuple containing a value of any time with a value 1
fn pair_with_one<T>(a: T) -> (T, i64) {
(a, 1)
}</T>
let pair1: (&str, i64) = pair_with_one(“hello”);
let pair2: (f64, i64) = pair_with_one(1.2345);
write a generic type class that contains two generic type vectors and write an example on how to use it
[derive(Debug)]
Main thing to note:
- impl<T> Default for tv<T> {</T></T>
- fn default() -> tv<T> {</T>
struct TwoVecs<T> {
pub first: Vec<T>,
pub second: Vec<T>,
}
impl<T> Default for TwoVecs<T> {
fn default() -> TwoVecs<T> {
TwoVecs {
first: Vec::default(), // type-inferred as Vec<T>
second: Vec::default(),
}
}
}</T></T></T></T></T></T></T>
let mut tv = TwoVecs::<i32>::default();
let mut tv = TwoVecs::default();
tv.first.push(1234);
println!("{:?}", tv); // TwoVecs { first: [1234], second: [] }</i32>
`
will this work:
fn is_larger<T>(a: T, b: T) -> bool {
a > b
}</T>
make it work:
- This function now takes two arguments (1) of the same type, (2) if that type implements Ord.
fn is_larger<T: Ord>(a: T, b: T) -> bool {
a > b
}
Monomorphization
compiles a version of a function for every “dynamic” type that the function is used for
will this work
fn is_larger<T: Ord>(a: T, b: T) -> bool {
a > b
}
yes
Week 10.0
implement IntoIterator for this struct
pub struct Container {
items: Vec<i32>,
}</i32>
impl Container {
pub fn new(items: Vec<i32>) -> Self {
Self { items }
}
}</i32>
TRAIT:
pub trait IntoIterator {
type Item;
type IntoIter: Iterator<Item = Self::Item>;
// Required method fn into_iter(self) -> Self::IntoIter; }
impl IntoIterator for Container {
type Item = i32;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter { self.items.into_iter() } }
[derive(Debug, Clone)]
implement add for GeoPoint
struct GeoPoint {
pub lat: f64,
pub lon: f64,
pub ele: i32,
}
pub trait Add<Rhs = Self> {
type Output;
// Required method fn add(self, rhs: Rhs) -> Self::Output; }
REMINDER:
- don’t forget to fill in Output
use std::ops::Add;
impl Add for GeoPoint {
type Output = GeoPoint;
fn add(self, other: GeoPoint) -> GeoPoint {
GeoPoint {
lat: self.lat + other.lat,
lon: self.lon + other.lon,
ele: self.ele + other.ele,
}
}
}
if you create a struct, can you compare it to different types?
yes
Implement PartialEq (==) for Geopoint:
impl PartialEq<GeoPoint> for GeoPoint {
fn eq(&self, other: &GeoPoint) -> bool {
self.lat == other.lat && self.lon == other.lon
}
}</GeoPoint>
understand how to read this:
impl<T, U, A1, A2> PartialEq<Vec<U, A2» for Vec<T, A1>
where
A1: Allocator,
A2: Allocator,
T: PartialEq<u>,</u>
impl<T, U, A1, A2> … : This part introduces type parameters T, U, A1, and A2. T and U will be the types of the elements in the vectors. A1 and A2 are allocator types. In Rust, you can specify custom allocators for Vec, although the standard library provides a global allocator that is used by default.
PartialEq<Vec<U, A2» for Vec<T, A1> : This is saying “I’m going to define how to check for equality between a Vec<T, A1> and a Vec<U, A2>”. PartialEq is the trait for types which have a partial equivalence relation, which is basically a fancy way of saying that you can compare them for equality.
where A1: Allocator, A2: Allocator, T: PartialEq<u> : This part is a set of constraints on the type parameters. It says "this implementation applies when A1 and A2 are types that implement the Allocator trait, and when values of type T can be compared for equality with values of type U".</u>
let v1: Vec<i32> = vec![1, 2, 3];
let v2: Vec<f64> = vec![1.0, 2.0, 3.0];</f64></i32>
if v1 == v2 {
println!(“The vectors are equal.”);
}
In this case, T is i32, U is f64, and A1 and A2 are the default allocators. The equality check works because i32 implements PartialEq<f64>.</f64>
what will this result in and understand why:
let v: Vec<i64> = Vec::from([1, 2, 3]);
let a: [i64; 3] = [1, 2, 3];
println!("{}", v == a);</i64>
let v: Vec<i64> = Vec::from([1, 2, 3]);
let a: [i64; 3] = [1, 2, 3];
println!("{}", v == a); // true</i64>
The comparison v == a prints true because Rust provides an implementation of the PartialEq trait for slices (and Vec<T> can deref to slices). This allows the comparison between a Vec<T> and an array.</T></T>
whats the destructor in rust
.drop()
write an example on how to user from and into using vec and array
- From: array of [1,2,3] to vector
- Into: array of [1,2,3] to vector
fn main() {
// Using From trait to convert an array into a Vec
let arr: [i32; 3] = [1, 2, 3];
let vec_from_array: Vec<i32> = Vec::from(arr);
println!("{:?}", vec_from_array); // prints: [1, 2, 3]</i32>
// Using Into trait to achieve the same thing let vec_from_array_2: Vec<i32> = arr.into(); println!("{:?}", vec_from_array_2); // prints: [1, 2, 3] }
answer the following quesitons for the Option Type:
- what is it used for
- what are the types
- what are the important methods that you can call on
- Used to represent a value that might be missing. Doesn’t feel like an error value, just a value that might be missing
- It can be either: Some(t), None
- Can use the following to check its state
is_some()
is_none()
implement find_elt and write a match case for this to print out weather it has been found or not:
let v1: Vec<i32> = Vec::from([4, 5, 2, 8, 7, 3, 1]);
let pos: Option<usize> = find_elt(&v1, 8);</usize></i32>
fn find_elt<T: PartialEq>(vec: &Vec<T>, element: T) -> Option<&T> {
for ele in vec.iter() {
if *ele == element {
return Some(ele);
}
}
None
}</T>
fn main() {
let v = vec![1, 2, 3, 4];
match find_elt(&v, 3) { Some(ele) => { println!("Found: {}", ele); } None => { println!("Element not found"); } } }
what are the properties for Result<T, E>? what are the important methods?
Don’t expect fail
Gives:
Ok(T)
Err(E)
useful methods:
.is_ok()
differences between Result and Option
Option is for null value, Result can be fore exeption
give this
let response: reqwest::blocking::Response;
match it so that if Err, prints err and exists
Ok, print ok
match res {
Err(e) => {
println!(“Request failed: {}”, e);
return;
}
Ok(r) => {
response = r;
}
}
let status = response.status();
println!(“Status code {}.”, status);
what are the properties of ? Operator
- Pass the error up to the parent
- Unchecked exception
- The ? operator can be used on any Result. The semantics: if Err, return the Err immediately; else .unwrap() and continue
given:
use reqwest::blocking::get;
use reqwest::Error as ReqwestError;
pub fn get<T: IntoUrl>(url: T) -> Result<Response></Response>
write a function that uses get and returns a Result object so that who ever calls this function has to deal with the error response.
should return response.text()
use reqwest::blocking::get;
use reqwest::Error as ReqwestError;
fn url_fetch(url: String) -> Result<String, ReqwestError> {
let response = get(url)?;
let text = response.text()?;
Ok(text)
}
Or
fn url_fetch_2(url: String) -> Result<String, ReqwestError> {
Ok(get(url)?.text()?)
}
how to avoid panicing when unwrapping
make sure that the Result object is Ok before unwrapping
Result vs Option
write a thread that returns a “hello word” string. take the thread output and print it
let h2 = thread::spawn(|| String::from(“Hello”) + “ world”);
println!(“h1 result: {:?}”, h1.join().unwrap());
understand closures with threads in rust
search it up
will this work, why or why not:
let data = Vec::from([1, 2, 3, 4, 5, 6, 7]);
let h = thread::spawn(|| {
println!(“The third element is {}”, data[3]);
});
h.join().unwrap();
closure may outlive the current function, but it borrowsdata
, which is owned by the current function
what would happen here:
let data = Vec::from([0, 1, 2, 3, 4, 5, 6, 7]);
let h = thread::spawn(move || {
println!(“The third element is {}”, data[3]);
});
println!(“{}”, data[0]);
h.join().unwrap();
let data = Vec::from([0, 1, 2, 3, 4, 5, 6, 7]);
let h = thread::spawn(move || {
println!(“The third element is {}”, data[3]);
});
println!(“{}”, data[0]); // compile error here
h.join().unwrap();
make this work:
let data = Vec::from([0, 1, 2, 3, 4, 5, 6, 7]);
let h = thread::spawn(move || {
println!(“The third element is {}”, data[3]);
});
println!(“{}”, data[0]);
h.join().unwrap();
let data = Vec::from([0, 1, 2, 3, 4, 5, 6, 7]);
let data_clone = data.clone(); // Create a clone of the data
let h = thread::spawn(move || {
println!(“The third element is {}”, data_clone[3]); // Use the clone in the spawned thread
});
println!(“{}”, data[0]); // Now you can use data
in the main thread as well
h.join().unwrap();
Implement a concurrent program using Rust’s mpsc (multi-producer, single consumer) channel, where three spawned threads each send their index multiplied by 10 to the main thread via the channel. Ensure the main thread receives and prints these values.
reminders:
- initialize using channel();
- receive th value using .recv().unwrap();
use std::sync::mpsc;
let (sender, receiver) = mpsc::channel();
for i in 0..3 {
let snd = sender.clone();
thread::spawn(move || {
snd.send(i * 10).unwrap();
});
}
for i in 0..3 {
println!(“”, receiver.recv());
}
what does the Send trait do
The trait marks a value that Rust can
transfer ownership to another thread. Most
types are Send.
what is the Sync trait
You have to know that they are imutable
- a trait that indicates a type where it’s safe
to share references between multiple threads. - Note: they must be immutable
what are the three rules for memory management
Ownership - every value is owned by exactly one variable
Safe borrowing with references:
Either multiple immutable borrows
Unique mutable borrow
References cannot outlive their values
zero-cost abstraction meaning
Reminder:
- know that option costs us run time performance
Almost none of these safety assurancecs cost us run-time performance.
except for Option
whats the word for a version of this function is compiled for each T that you use it with.
monomorphization
will this compile:
struct Node<T> {
value: T,
left: Option<Node<T>>,
right: Option<Node<T>>,
}</T></T></T>
no, the size of a type must be known at compile time. In this case, the size of Node<T> cannot be determined because it recursively includes itself in its own definition.</T>
write a struct for a binary tree Node<T></T>
struct Node<T> {
value: T,
left: Option<Box<Node<T>>>,
right: Option<Box<Node<T>>>,
}</T></T></T>
why doesnt this work:
struct Node<T> {
pub val: T;
pub left: Node<T>;
pub right: Node<T>;
}</T></T></T>
- The reason is that Rust’s memory safety rules do not allow data structures with circular references without some form of indirection
- the code as written does not specify lifetimes for the references, which would be needed for the code to compile. Lifetimes in Rust are a way of ensuring that references are valid for the duration they’re being used.
Here’s an example of how you might modify your code to use lifetimes and still compile:
struct Node<’a, T: ‘a> {
value: T,
left: Option<&’a Node<’a, T»,
right: Option<&’a Node<’a, T»,
}
However, this would still not allow for creating circular structures or self-referential structures because of Rust’s ownership rules.
what are lifetime references
Lifetimes in Rust are used to prevent dangling references. That is, Rust uses lifetimes to ensure that all references are always valid. Rust does this by checking that the lifetime of any reference does not exceed the lifetime of the object it references.
what does Box<T> do</T>
- A Box can hold another value, takes ownership of that value, and makes sure the value lives as long as the Box exists
- When a Box gets dropped, its contents will be
dropped as well (because it owns the
contents). - is not a copy but is a clone. clone -> clones the entire content of the box
- auto dereferenced when printing boxes, or explicitly: let f2: f64 = *b2;
- instantiate by: let f1 = 1.234; .rs
let b1 = Box::new(f1); - The Box does implement AsMut and whenever
we did mutable things inside a Box, there was
an implicit dereference using .as_mut().
implement the following methods for Node:
- new
- set_left
- set_right
struct Node<T> {
value: T,
left: Option<Node<T>>,
right: Option<Node<T>>,
}`</T></T></T>
Reminders:
- Box::new(…)
- Some(Box::new(Node::new(value)));
impl<T> Node<T> { .rs
fn new(value: T) -> Node<T> {
Node {
value,
left: None,
right: None,
}
}
fn set_left(&mut self, value: T) {
self.left =
Some(Box::new(Node::new(value)));
}
fn set_right(&mut self, value: T) {
self.right =
Some(Box::new(Node::new(value)));
}
}</T></T></T>
let mut n = Node::new(10); .rs
n.set_left(5);
n.set_right(15);
println!(“”, n);
what should you use for multiple immutable references? and what can you do with it?
Rc<T></T>
Rc can be safely cloned and is guaranteed
to live as long as the contents are accessible.
It owns its contents, and drops them when
zero references remain.
use std::rc::Rc; .rs
let mut v = Vec::from([1, 2]);
v.push(3); // can get “&mut v” here
let r1 = Rc::new(v);
let r2 = r1.clone();
- what does .iter() do?
- map the values in vec to times it by 2 and generate a new vec of it.
Reminder:
- the map must take in a &reference
What it does: Creates an immutable iterator over the collection.
let v = vec![1, 2, 3];
for i in v.iter() {
println!(“{}”, i);
}
FUNCTIONAL
let v = vec![1, 2, 3];
let doubled: Vec<_> = v.iter().map(|&x| x * 2).collect();
println!("{:?}", doubled); // [2, 4, 6]</_>
understand .fold()
fn fold<B, F>(self, init: B, f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
let v = vec![1, 2, 3, 4];
let product = v.iter().fold(1, |acc, &x| acc * x);
println!(“Product: {}”, product); // 24
what is Arc
same as Rc but thread safe
what kind of pointer should you use if you want multiple references to an object on the heap
mutex, Arc, Rc
Type Sharable? Mutable? Thread Safe?
&
&mut
Box
Rc
Arc
RefCell
Mutex
Type Sharable? Mutable?Thread Safe?
& yes * no no
&mut no * yes no
Box no yes no
Rc yes no no
Arc yes no yes
RefCell yes ** yes no
Mutex yes, in Arc yes yes
- but doesn’t own contents, so lifetime
restrictions.
** while there is no mutable borrow
how would you create a shared mutable state data structure?
give it to multiple threads that adds some value to it.
know how to destroy it
what would be the type of it?
Key things to remember:
- let mut val = values.lock().unwrap(); // MUST BE MUT
Algorithm:
- let value = Vec::new();
- let sharable_v = Arc::new(Mutex::new(value));
- clone shareble_v and pass it to thread
- store the handle and wait for all threads to finish by calling .join().unwrap() on them
- print it.
CREATE MUTABLE STATE STRUCTURE
let values = Vec::new();
let values_shared =
Arc::new(Mutex::new(values));
let mut handles = Vec::new();
PASS THEM TO THREADS
for i in 1..6 { .rs
let my_values_shared =
values_shared.clone();
let h = thread::spawn(move || {
add_some_values(my_values_shared, i);
});
handles.push(h);
}
ADD VALUE
fn add_some_values(values: .rs
Arc<Mutex<Vec<i64>>>, thread_id: i64) {
let mut rng = rand::thread_rng();
for _ in 0..3 {
let v = rng.gen_range(0..100);
{
let mut vals =
values.lock().unwrap();
vals.push(v + thread_id * 1000);
}
let delay = rng.gen_range(100..200);</i64>
thread::sleep(Duration::from_millis(delay));
}
DESTROY
let mutex =
Arc::into_inner(values_shared).unwrap();
let final_values = mutex.into_inner().unwrap();
println!(“”, final_values)
MUTEX TYPE
Arc<Mutex<Vec<i64>>></i64>
Write the following out:
1. create a trait Can speak
2. create a struct speaker, and implement CanSpeak for it that prints “hello”
3. create a function called “speak_twice” that takes any object that implements the CanSpeak object dynamically, that calles the function .speak() twice
4. run the program to call speak_twice on the box
trait CanSpeak {
fn speak(&self) -> ();
}
struct Speaker {}
impl CanSpeak for Speaker {
fn speak(&self) -> () {
println!(“hello”);
}
}
fn speak_twice(s: &Box<dyn>) { .rs
s.speak();
s.speak();
}</dyn>
let s: Box<dyn> = Box::new(Speaker .rs
{});
s.speak();
speak_twice(&s);</dyn>