Compare commits

..

9 commits

Author SHA1 Message Date
mo8it d768353806 Fix typo 2024-06-21 18:29:00 +02:00
mo8it e4dbbbf5f5 Remove move_semantics4, add rest of move_semantics solutions 2024-06-21 18:14:19 +02:00
mo8it fd558065c7 move_semantics3 solution 2024-06-21 17:04:51 +02:00
mo8it 68142aff7f move_semantics2 solution 2024-06-21 17:02:50 +02:00
mo8it 946c29679e move_semantics1 solution 2024-06-21 16:16:52 +02:00
mo8it 6a79ada7f2 Add comment to vecs2 2024-06-21 15:06:50 +02:00
mo8it 835ec72622 vecs2 solution + significant change to have a better comparison between both methods 2024-06-21 14:52:11 +02:00
mo8it a9f0c7bf1f vecs1 solution 2024-06-20 01:00:06 +02:00
mo8it 0abcdeed42 primitive_types6 solution 2024-06-19 14:25:29 +02:00
19 changed files with 307 additions and 165 deletions

View file

@ -1,21 +1,17 @@
// Use a tuple index to access the second element of `numbers`. You can put the
// expression for the second element where ??? is so that the test passes.
fn main() { fn main() {
// You can optionally experiment here. // You can optionally experiment here.
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*;
#[test] #[test]
fn indexing_tuple() { fn indexing_tuple() {
let numbers = (1, 2, 3); let numbers = (1, 2, 3);
// Replace below ??? with the tuple indexing syntax.
let second = ???;
assert_eq!(2, second, // TODO: Use a tuple index to access the second element of `numbers`
"This is not the 2nd number in the tuple!") // and assign it to a variable called `second`.
// let second = ???;
assert_eq!(second, 2, "This is not the 2nd number in the tuple!");
} }
} }

View file

@ -1,11 +1,9 @@
// Your task is to create a `Vec` which holds the exact same elements as in the
// array `a`.
//
// Make me compile and pass the test!
fn array_and_vec() -> ([i32; 4], Vec<i32>) { fn array_and_vec() -> ([i32; 4], Vec<i32>) {
let a = [10, 20, 30, 40]; // a plain array let a = [10, 20, 30, 40]; // Array
let v = // TODO: declare your vector here with the macro for vectors
// TODO: Create a vector called `v` which contains the exact same elements as in the array `a`.
// Use the vector macro.
// let v = ???;
(a, v) (a, v)
} }
@ -21,6 +19,6 @@ mod tests {
#[test] #[test]
fn test_array_and_vec_similarity() { fn test_array_and_vec_similarity() {
let (a, v) = array_and_vec(); let (a, v) = array_and_vec();
assert_eq!(a, v[..]); assert_eq!(a, *v);
} }
} }

View file

@ -1,25 +1,32 @@
// A Vec of even numbers is given. Your task is to complete the loop so that fn vec_loop(input: &[i32]) -> Vec<i32> {
// each number in the Vec is multiplied by 2. let mut output = Vec::new();
//
// Make me pass the test!
fn vec_loop(mut v: Vec<i32>) -> Vec<i32> { for element in input {
for element in v.iter_mut() { // TODO: Multiply each element in the `input` slice by 2 and push it to
// TODO: Fill this up so that each element in the Vec `v` is // the `output` vector.
// multiplied by 2.
???
} }
// At this point, `v` should be equal to [4, 8, 12, 16, 20]. output
v
} }
fn vec_map(v: &Vec<i32>) -> Vec<i32> { fn vec_map_example(input: &[i32]) -> Vec<i32> {
v.iter().map(|element| { // An example of collecting a vector after mapping.
// TODO: Do the same thing as above - but instead of mutating the // We map each element of the `input` slice to its value plus 1.
// Vec, you can just return the new number! // If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`.
??? input.iter().map(|element| element + 1).collect()
}).collect() }
fn vec_map(input: &[i32]) -> Vec<i32> {
// TODO: Here, we also want to multiply each element in the `input` slice
// by 2, but with iterator mapping instead of manually pushing into an empty
// vector.
// See the example in the function `vec_map_example` above.
input
.iter()
.map(|element| {
// ???
})
.collect()
} }
fn main() { fn main() {
@ -32,17 +39,22 @@ mod tests {
#[test] #[test]
fn test_vec_loop() { fn test_vec_loop() {
let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect(); let input = [2, 4, 6, 8, 10];
let ans = vec_loop(v.clone()); let ans = vec_loop(&input);
assert_eq!(ans, [4, 8, 12, 16, 20]);
}
assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>()); #[test]
fn test_vec_map_example() {
let input = [1, 2, 3];
let ans = vec_map_example(&input);
assert_eq!(ans, [2, 3, 4]);
} }
#[test] #[test]
fn test_vec_map() { fn test_vec_map() {
let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect(); let input = [2, 4, 6, 8, 10];
let ans = vec_map(&v); let ans = vec_map(&input);
assert_eq!(ans, [4, 8, 12, 16, 20]);
assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>());
} }
} }

View file

@ -1,3 +1,4 @@
// TODO: Fix the compiler error in this function.
fn fill_vec(vec: Vec<i32>) -> Vec<i32> { fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
let vec = vec; let vec = vec;
@ -17,9 +18,7 @@ mod tests {
#[test] #[test]
fn move_semantics1() { fn move_semantics1() {
let vec0 = vec![22, 44, 66]; let vec0 = vec![22, 44, 66];
let vec1 = fill_vec(vec0); let vec1 = fill_vec(vec0);
assert_eq!(vec1, vec![22, 44, 66, 88]); assert_eq!(vec1, vec![22, 44, 66, 88]);
} }
} }

View file

@ -1,5 +1,3 @@
// Make the test pass by finding a way to keep both Vecs separate!
fn fill_vec(vec: Vec<i32>) -> Vec<i32> { fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
let mut vec = vec; let mut vec = vec;
@ -16,13 +14,15 @@ fn main() {
mod tests { mod tests {
use super::*; use super::*;
// TODO: Make both vectors `vec0` and `vec1` accessible at the same time to
// fix the compiler error in the test.
#[test] #[test]
fn move_semantics2() { fn move_semantics2() {
let vec0 = vec![22, 44, 66]; let vec0 = vec![22, 44, 66];
let vec1 = fill_vec(vec0); let vec1 = fill_vec(vec0);
assert_eq!(vec0, vec![22, 44, 66]); assert_eq!(vec0, [22, 44, 66]);
assert_eq!(vec1, vec![22, 44, 66, 88]); assert_eq!(vec1, [22, 44, 66, 88]);
} }
} }

View file

@ -1,6 +1,4 @@
// Make me compile without adding new lines -- just changing existing lines! (no // TODO: Fix the compiler error in the function without adding any new line.
// lines with multiple semicolons necessary!)
fn fill_vec(vec: Vec<i32>) -> Vec<i32> { fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
vec.push(88); vec.push(88);
@ -18,9 +16,7 @@ mod tests {
#[test] #[test]
fn move_semantics3() { fn move_semantics3() {
let vec0 = vec![22, 44, 66]; let vec0 = vec![22, 44, 66];
let vec1 = fill_vec(vec0); let vec1 = fill_vec(vec0);
assert_eq!(vec1, [22, 44, 66, 88]);
assert_eq!(vec1, vec![22, 44, 66, 88]);
} }
} }

View file

@ -1,31 +1,18 @@
// Refactor this code so that instead of passing `vec0` into the `fill_vec`
// function, the Vector gets created in the function itself and passed back to
// the test function.
// `fill_vec()` no longer takes `vec: Vec<i32>` as argument - don't change this!
fn fill_vec() -> Vec<i32> {
// Instead, let's create and fill the Vec in here - how do you do that?
let mut vec = vec;
vec.push(88);
vec
}
fn main() { fn main() {
// You can optionally experiment here. // You can optionally experiment here.
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; // TODO: Fix the compiler errors only by reordering the lines in the test.
// Don't add, change or remove any line.
#[test] #[test]
fn move_semantics4() { fn move_semantics5() {
let vec0 = vec![22, 44, 66]; let mut x = 100;
let y = &mut x;
let vec1 = fill_vec(vec0); let z = &mut x;
*y += 100;
assert_eq!(vec1, vec![22, 44, 66, 88]); *z += 1000;
assert_eq!(x, 1200);
} }
} }

View file

@ -1,21 +1,22 @@
// Make me compile only by reordering the lines in the test, but without adding, // TODO: Fix the compiler errors without changing anything except adding or
// changing or removing any of them. // removing references (the character `&`).
fn main() { fn main() {
// You can optionally experiment here. let data = "Rust is great!".to_string();
get_char(data);
string_uppercase(&data);
} }
#[cfg(test)] // Shouldn't take ownership
mod tests { fn get_char(data: String) -> char {
use super::*; data.chars().last().unwrap()
}
#[test] // Should take ownership
fn move_semantics5() { fn string_uppercase(mut data: &String) {
let mut x = 100; data = &data.to_uppercase();
let y = &mut x;
let z = &mut x; println!("{data}");
*y += 100;
*z += 1000;
assert_eq!(x, 1200);
}
} }

View file

@ -1,21 +0,0 @@
// You can't change anything except adding or removing references.
fn main() {
let data = "Rust is great!".to_string();
get_char(data);
string_uppercase(&data);
}
// Should not take ownership
fn get_char(data: String) -> char {
data.chars().last().unwrap()
}
// Should take ownership
fn string_uppercase(mut data: &String) {
data = &data.to_uppercase();
println!("{}", data);
}

View file

@ -296,7 +296,7 @@ While you could use a destructuring `let` for the tuple here, try
indexing into it instead, as explained in the last example of the indexing into it instead, as explained in the last example of the
'Data Types -> The Tuple Type' section of the book: 'Data Types -> The Tuple Type' section of the book:
https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type
Now you have another tool in your toolbox!""" Now, you have another tool in your toolbox!"""
# VECS # VECS
@ -307,8 +307,9 @@ hint = """
In Rust, there are two ways to define a Vector. In Rust, there are two ways to define a Vector.
1. One way is to use the `Vec::new()` function to create a new vector 1. One way is to use the `Vec::new()` function to create a new vector
and fill it with the `push()` method. and fill it with the `push()` method.
2. The second way, which is simpler is to use the `vec![]` macro and 2. The second way is to use the `vec![]` macro and define your elements
define your elements inside the square brackets. inside the square brackets. This way is simpler when you exactly know
the initial values.
Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html
of the Rust book to learn more. of the Rust book to learn more.
@ -318,15 +319,10 @@ of the Rust book to learn more.
name = "vecs2" name = "vecs2"
dir = "05_vecs" dir = "05_vecs"
hint = """ hint = """
In the first function we are looping over the Vector and getting a reference to In the first function, we create an empty vector and want to push new elements
one `element` at a time. to it.
To modify the value of that `element` we need to use the `*` dereference In the second function, we map the values of the input and collect them into a vector.
operator. You can learn more in this chapter of the Rust book:
https://doc.rust-lang.org/stable/book/ch08-01-vectors.html#iterating-over-the-values-in-a-vector
In the second function this dereferencing is not necessary, because the `map`
function expects the new value to be returned.
After you've completed both functions, decide for yourself which approach you After you've completed both functions, decide for yourself which approach you
like better. like better.
@ -346,8 +342,7 @@ error on the line where we push an element to the vector, right?
The fix for this is going to be adding one keyword, and the addition is NOT on The fix for this is going to be adding one keyword, and the addition is NOT on
the line where we push to the vector (where the error is). the line where we push to the vector (where the error is).
Also: Try accessing `vec0` after having called `fill_vec()`. See what Try accessing `vec0` after having called `fill_vec()`. See what happens!"""
happens!"""
[[exercises]] [[exercises]]
name = "move_semantics2" name = "move_semantics2"
@ -357,16 +352,10 @@ When running this exercise for the first time, you'll notice an error about
"borrow of moved value". In Rust, when an argument is passed to a function and "borrow of moved value". In Rust, when an argument is passed to a function and
it's not explicitly returned, you can't use the original variable anymore. it's not explicitly returned, you can't use the original variable anymore.
We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's
being "moved" into `vec1`, meaning we can't access `vec0` anymore after the being "moved" into `vec1`, meaning we can't access `vec0` anymore.
fact.
Rust provides a couple of different ways to mitigate this issue, feel free to You could make another, separate version of the data that's in `vec0` and
try them all: pass it to `fill_vec` instead.
1. You could make another, separate version of the data that's in `vec0` and
pass that to `fill_vec` instead.
2. Make `fill_vec` borrow its argument instead of taking ownership of it,
and then copy the data within the function (`vec.clone()`) in order to
return an owned `Vec<i32>`.
""" """
[[exercises]] [[exercises]]
@ -382,28 +371,15 @@ an existing binding to be a mutable binding instead of an immutable one :)"""
name = "move_semantics4" name = "move_semantics4"
dir = "06_move_semantics" dir = "06_move_semantics"
hint = """ hint = """
Stop reading whenever you feel like you have enough direction :) Or try
doing one step and then fixing the compiler errors that result!
So the end goal is to:
- get rid of the first line in main that creates the new vector
- so then `vec0` doesn't exist, so we can't pass it to `fill_vec`
- `fill_vec` has had its signature changed, which our call should reflect
- since we're not creating a new vec in `main` anymore, we need to create
a new vec in `fill_vec`, and fill it with the expected values"""
[[exercises]]
name = "move_semantics5"
dir = "06_move_semantics"
hint = """
Carefully reason about the range in which each mutable reference is in Carefully reason about the range in which each mutable reference is in
scope. Does it help to update the value of referent (`x`) immediately after scope. Does it help to update the value of referent (`x`) immediately after
the mutable reference is taken? Read more about 'Mutable References' the mutable reference is taken?
in the book's section 'References and Borrowing': Read more about 'Mutable References' in the book's section 'References and Borrowing':
https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references. https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references.
""" """
[[exercises]] [[exercises]]
name = "move_semantics6" name = "move_semantics5"
dir = "06_move_semantics" dir = "06_move_semantics"
test = false test = false
hint = """ hint = """
@ -412,14 +388,10 @@ https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html
The first problem is that `get_char` is taking ownership of the string. So The first problem is that `get_char` is taking ownership of the string. So
`data` is moved and can't be used for `string_uppercase`. `data` is moved to `data` is moved and can't be used for `string_uppercase`. `data` is moved to
`get_char` first, meaning that `string_uppercase` cannot manipulate the data. `get_char` first, meaning that `string_uppercase` can't manipulate the data.
Once you've fixed that, `string_uppercase`'s function signature will also need Once you've fixed that, `string_uppercase`'s function signature will also need
to be adjusted. to be adjusted."""
Can you figure out how?
Another hint: it has to do with the `&` character."""
# STRUCTS # STRUCTS

View file

@ -1 +1,16 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
#[test]
fn indexing_tuple() {
let numbers = (1, 2, 3);
// Tuple indexing syntax.
let second = numbers.1;
assert_eq!(second, 2, "This is not the 2nd number in the tuple!");
}
}

View file

@ -1 +1,23 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 fn array_and_vec() -> ([i32; 4], Vec<i32>) {
let a = [10, 20, 30, 40]; // Array
// Used the `vec!` macro.
let v = vec![10, 20, 30, 40];
(a, v)
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_array_and_vec_similarity() {
let (a, v) = array_and_vec();
assert_eq!(a, *v);
}
}

View file

@ -1 +1,55 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 fn vec_loop(input: &[i32]) -> Vec<i32> {
let mut output = Vec::new();
for element in input {
output.push(2 * element);
}
output
}
fn vec_map_example(input: &[i32]) -> Vec<i32> {
// An example of collecting a vector after mapping.
// We map each element of the `input` slice to its value plus 1.
// If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`.
input.iter().map(|element| element + 1).collect()
}
fn vec_map(input: &[i32]) -> Vec<i32> {
// We will dive deeper into iterators, but for now, this is all what you
// had to do!
// Advanced note: This method is more efficient because it automatically
// preallocates enough capacity. This can be done manually in `vec_loop`
// using `Vec::with_capacity(input.len())` instead of `Vec::new()`.
input.iter().map(|element| 2 * element).collect()
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vec_loop() {
let input = [2, 4, 6, 8, 10];
let ans = vec_loop(&input);
assert_eq!(ans, [4, 8, 12, 16, 20]);
}
#[test]
fn test_vec_map_example() {
let input = [1, 2, 3];
let ans = vec_map_example(&input);
assert_eq!(ans, [2, 3, 4]);
}
#[test]
fn test_vec_map() {
let input = [2, 4, 6, 8, 10];
let ans = vec_map(&input);
assert_eq!(ans, [4, 8, 12, 16, 20]);
}
}

View file

@ -1 +1,25 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
let mut vec = vec;
// ^^^ added
vec.push(88);
vec
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn move_semantics1() {
let vec0 = vec![22, 44, 66];
let vec1 = fill_vec(vec0);
// `vec0` can't be accessed anymore because it is moved to `fill_vec`.
assert_eq!(vec1, vec![22, 44, 66, 88]);
}
}

View file

@ -1 +1,28 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
let mut vec = vec;
vec.push(88);
vec
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn move_semantics2() {
let vec0 = vec![22, 44, 66];
// Cloning `vec0` so that the clone is moved into `fill_vec`, not `vec0`
// itself.
let vec1 = fill_vec(vec0.clone());
assert_eq!(vec0, [22, 44, 66]);
assert_eq!(vec1, [22, 44, 66, 88]);
}
}

View file

@ -1 +1,22 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 fn fill_vec(mut vec: Vec<i32>) -> Vec<i32> {
// ^^^ added
vec.push(88);
vec
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn move_semantics3() {
let vec0 = vec![22, 44, 66];
let vec1 = fill_vec(vec0);
assert_eq!(vec1, [22, 44, 66, 88]);
}
}

View file

@ -1 +1,21 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
// TODO: Fix the compiler errors only by reordering the lines in the test.
// Don't add, change or remove any line.
#[test]
fn move_semantics5() {
let mut x = 100;
let y = &mut x;
// `y` used here.
*y += 100;
// The mutable reference `y` is not used anymore,
// therefore a new reference can be created.
let z = &mut x;
*z += 1000;
assert_eq!(x, 1200);
}
}

View file

@ -1 +1,21 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰 fn main() {
let data = "Rust is great!".to_string();
get_char(&data);
string_uppercase(data);
}
// Borrows instead of taking ownership.
// It is recommended to use `&str` instead of `&String` here. But this is
// enough for now because we didn't handle strings yet.
fn get_char(data: &String) -> char {
data.chars().last().unwrap()
}
// Takes ownership instead of borrowing.
fn string_uppercase(mut data: String) {
data = data.to_uppercase();
println!("{data}");
}

View file

@ -1 +0,0 @@
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰