Compare commits

..

No commits in common. "d768353806f905989b4cc29cd7a97891cbbf8ec3" and "532c9ebb30afa226590e68e87af11da42b598974" have entirely different histories.

19 changed files with 164 additions and 306 deletions

View file

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

View file

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

View file

@ -1,32 +1,25 @@
fn vec_loop(input: &[i32]) -> Vec<i32> {
let mut output = Vec::new();
// A Vec of even numbers is given. Your task is to complete the loop so that
// each number in the Vec is multiplied by 2.
//
// Make me pass the test!
for element in input {
// TODO: Multiply each element in the `input` slice by 2 and push it to
// the `output` vector.
fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {
for element in v.iter_mut() {
// TODO: Fill this up so that each element in the Vec `v` is
// multiplied by 2.
???
}
output
// At this point, `v` should be equal to [4, 8, 12, 16, 20].
v
}
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> {
// 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 vec_map(v: &Vec<i32>) -> Vec<i32> {
v.iter().map(|element| {
// TODO: Do the same thing as above - but instead of mutating the
// Vec, you can just return the new number!
???
}).collect()
}
fn main() {
@ -39,22 +32,17 @@ mod tests {
#[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]);
}
let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect();
let ans = vec_loop(v.clone());
#[test]
fn test_vec_map_example() {
let input = [1, 2, 3];
let ans = vec_map_example(&input);
assert_eq!(ans, [2, 3, 4]);
assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>());
}
#[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]);
let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect();
let ans = vec_map(&v);
assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>());
}
}

View file

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

View file

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

View file

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

View file

@ -1,18 +1,31 @@
// 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() {
// 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.
use super::*;
#[test]
fn move_semantics5() {
let mut x = 100;
let y = &mut x;
let z = &mut x;
*y += 100;
*z += 1000;
assert_eq!(x, 1200);
fn move_semantics4() {
let vec0 = vec![22, 44, 66];
let vec1 = fill_vec(vec0);
assert_eq!(vec1, vec![22, 44, 66, 88]);
}
}

View file

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

View file

@ -0,0 +1,21 @@
// 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
'Data Types -> The Tuple Type' section of the book:
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
@ -307,9 +307,8 @@ hint = """
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
and fill it with the `push()` method.
2. The second way is to use the `vec![]` macro and define your elements
inside the square brackets. This way is simpler when you exactly know
the initial values.
2. The second way, which is simpler is to use the `vec![]` macro and
define your elements inside the square brackets.
Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html
of the Rust book to learn more.
@ -319,10 +318,15 @@ of the Rust book to learn more.
name = "vecs2"
dir = "05_vecs"
hint = """
In the first function, we create an empty vector and want to push new elements
to it.
In the first function we are looping over the Vector and getting a reference to
one `element` at a time.
In the second function, we map the values of the input and collect them into a vector.
To modify the value of that `element` we need to use the `*` dereference
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
like better.
@ -342,7 +346,8 @@ 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 line where we push to the vector (where the error is).
Try accessing `vec0` after having called `fill_vec()`. See what happens!"""
Also: Try accessing `vec0` after having called `fill_vec()`. See what
happens!"""
[[exercises]]
name = "move_semantics2"
@ -352,10 +357,16 @@ 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
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
being "moved" into `vec1`, meaning we can't access `vec0` anymore.
being "moved" into `vec1`, meaning we can't access `vec0` anymore after the
fact.
You could make another, separate version of the data that's in `vec0` and
pass it to `fill_vec` instead.
Rust provides a couple of different ways to mitigate this issue, feel free to
try them all:
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]]
@ -371,15 +382,28 @@ an existing binding to be a mutable binding instead of an immutable one :)"""
name = "move_semantics4"
dir = "06_move_semantics"
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
scope. Does it help to update the value of referent (`x`) immediately after
the mutable reference is taken?
Read more about 'Mutable References' in the book's section 'References and Borrowing':
the mutable reference is taken? 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.
"""
[[exercises]]
name = "move_semantics5"
name = "move_semantics6"
dir = "06_move_semantics"
test = false
hint = """
@ -388,10 +412,14 @@ 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
`data` is moved and can't be used for `string_uppercase`. `data` is moved to
`get_char` first, meaning that `string_uppercase` can't manipulate the data.
`get_char` first, meaning that `string_uppercase` cannot manipulate the data.
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

View file

@ -1,16 +1 @@
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!");
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,23 +1 @@
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);
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,55 +1 @@
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]);
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,25 +1 @@
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]);
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,28 +1 @@
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]);
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,22 +1 @@
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]);
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,21 +1 @@
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);
}
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

@ -1,21 +1 @@
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}");
}
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰

View file

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