mirror of
https://github.com/rust-lang/rustlings.git
synced 2026-01-02 16:59:18 +00:00
23th completed
This commit is contained in:
parent
de07320d0c
commit
27860807eb
@ -1,6 +1,6 @@
|
|||||||
DON'T EDIT THIS FILE!
|
DON'T EDIT THIS FILE!
|
||||||
|
|
||||||
box1
|
as_ref_mut
|
||||||
|
|
||||||
intro1
|
intro1
|
||||||
intro2
|
intro2
|
||||||
@ -77,3 +77,22 @@ iterators2
|
|||||||
iterators3
|
iterators3
|
||||||
iterators4
|
iterators4
|
||||||
iterators5
|
iterators5
|
||||||
|
box1
|
||||||
|
rc1
|
||||||
|
arc1
|
||||||
|
cow1
|
||||||
|
threads1
|
||||||
|
threads2
|
||||||
|
threads3
|
||||||
|
macros1
|
||||||
|
macros2
|
||||||
|
macros3
|
||||||
|
macros4
|
||||||
|
clippy1
|
||||||
|
clippy2
|
||||||
|
clippy3
|
||||||
|
using_as
|
||||||
|
from_into
|
||||||
|
from_str
|
||||||
|
try_from_into
|
||||||
|
as_ref_mut
|
||||||
@ -23,13 +23,13 @@ fn main() {
|
|||||||
let numbers: Vec<_> = (0..100u32).collect();
|
let numbers: Vec<_> = (0..100u32).collect();
|
||||||
|
|
||||||
// TODO: Define `shared_numbers` by using `Arc`.
|
// TODO: Define `shared_numbers` by using `Arc`.
|
||||||
// let shared_numbers = ???;
|
let shared_numbers = Arc::new(numbers);
|
||||||
|
|
||||||
let mut join_handles = Vec::new();
|
let mut join_handles = Vec::new();
|
||||||
|
|
||||||
for offset in 0..8 {
|
for offset in 0..8 {
|
||||||
// TODO: Define `child_numbers` using `shared_numbers`.
|
// TODO: Define `child_numbers` using `shared_numbers`.
|
||||||
// let child_numbers = ???;
|
let child_numbers = Arc::clone(&shared_numbers);
|
||||||
|
|
||||||
let handle = thread::spawn(move || {
|
let handle = thread::spawn(move || {
|
||||||
let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum();
|
let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum();
|
||||||
|
|||||||
@ -12,18 +12,18 @@
|
|||||||
// TODO: Use a `Box` in the enum definition to make the code compile.
|
// TODO: Use a `Box` in the enum definition to make the code compile.
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
enum List {
|
enum List {
|
||||||
Cons(i32, List),
|
Cons(i32, Box<List>),
|
||||||
Nil,
|
Nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Create an empty cons list.
|
// TODO: Create an empty cons list.
|
||||||
fn create_empty_list() -> List {
|
fn create_empty_list() -> List {
|
||||||
todo!()
|
List::Nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Create a non-empty cons list.
|
// TODO: Create a non-empty cons list.
|
||||||
fn create_non_empty_list() -> List {
|
fn create_non_empty_list() -> List {
|
||||||
todo!()
|
List::Cons(1, Box::new(List::Nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
@ -39,7 +39,7 @@ mod tests {
|
|||||||
let mut input = Cow::from(&vec);
|
let mut input = Cow::from(&vec);
|
||||||
abs_all(&mut input);
|
abs_all(&mut input);
|
||||||
// TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
|
// TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
|
||||||
assert!(matches!(input, todo!()));
|
assert!(matches!(input,Cow::Borrowed(_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -52,7 +52,7 @@ mod tests {
|
|||||||
let mut input = Cow::from(vec);
|
let mut input = Cow::from(vec);
|
||||||
abs_all(&mut input);
|
abs_all(&mut input);
|
||||||
// TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
|
// TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
|
||||||
assert!(matches!(input, todo!()));
|
assert!(matches!(input, Cow::Owned(_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -64,6 +64,6 @@ mod tests {
|
|||||||
let mut input = Cow::from(vec);
|
let mut input = Cow::from(vec);
|
||||||
abs_all(&mut input);
|
abs_all(&mut input);
|
||||||
// TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
|
// TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
|
||||||
assert!(matches!(input, todo!()));
|
assert!(matches!(input,Cow::Owned(_)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,17 +60,17 @@ mod tests {
|
|||||||
jupiter.details();
|
jupiter.details();
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
let saturn = Planet::Saturn(Rc::new(Sun));
|
let saturn = Planet::Saturn(Rc::clone(&sun));
|
||||||
println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
|
println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
|
||||||
saturn.details();
|
saturn.details();
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
let uranus = Planet::Uranus(Rc::new(Sun));
|
let uranus = Planet::Uranus(Rc::clone(&sun));
|
||||||
println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
|
println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
|
||||||
uranus.details();
|
uranus.details();
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
let neptune = Planet::Neptune(Rc::new(Sun));
|
let neptune = Planet::Neptune(Rc::clone(&sun));
|
||||||
println!("reference count = {}", Rc::strong_count(&sun)); // 9 references
|
println!("reference count = {}", Rc::strong_count(&sun)); // 9 references
|
||||||
neptune.details();
|
neptune.details();
|
||||||
|
|
||||||
@ -92,12 +92,17 @@ mod tests {
|
|||||||
println!("reference count = {}", Rc::strong_count(&sun)); // 4 references
|
println!("reference count = {}", Rc::strong_count(&sun)); // 4 references
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
|
drop(mercury);
|
||||||
|
|
||||||
println!("reference count = {}", Rc::strong_count(&sun)); // 3 references
|
println!("reference count = {}", Rc::strong_count(&sun)); // 3 references
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
|
drop(venus);
|
||||||
|
|
||||||
println!("reference count = {}", Rc::strong_count(&sun)); // 2 references
|
println!("reference count = {}", Rc::strong_count(&sun)); // 2 references
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
|
drop(earth);
|
||||||
println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference
|
println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference
|
||||||
|
|
||||||
assert_eq!(Rc::strong_count(&sun), 1);
|
assert_eq!(Rc::strong_count(&sun), 1);
|
||||||
|
|||||||
@ -24,6 +24,12 @@ fn main() {
|
|||||||
for handle in handles {
|
for handle in handles {
|
||||||
// TODO: Collect the results of all threads into the `results` vector.
|
// TODO: Collect the results of all threads into the `results` vector.
|
||||||
// Use the `JoinHandle` struct which is returned by `thread::spawn`.
|
// Use the `JoinHandle` struct which is returned by `thread::spawn`.
|
||||||
|
|
||||||
|
match handle.join() {
|
||||||
|
Ok(val) => results.push(val),
|
||||||
|
Err(_)=>results.push(0)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if results.len() != 10 {
|
if results.len() != 10 {
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
// work. But this time, the spawned threads need to be in charge of updating a
|
// work. But this time, the spawned threads need to be in charge of updating a
|
||||||
// shared value: `JobStatus.jobs_done`
|
// shared value: `JobStatus.jobs_done`
|
||||||
|
|
||||||
use std::{sync::Arc, thread, time::Duration};
|
use std::{sync::{Arc, Mutex}, thread, time::Duration};
|
||||||
|
|
||||||
struct JobStatus {
|
struct JobStatus {
|
||||||
jobs_done: u32,
|
jobs_done: u32,
|
||||||
@ -10,7 +10,7 @@ struct JobStatus {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// TODO: `Arc` isn't enough if you want a **mutable** shared state.
|
// TODO: `Arc` isn't enough if you want a **mutable** shared state.
|
||||||
let status = Arc::new(JobStatus { jobs_done: 0 });
|
let status = Arc::new(Mutex::new(JobStatus { jobs_done: 0 }));
|
||||||
|
|
||||||
let mut handles = Vec::new();
|
let mut handles = Vec::new();
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
@ -19,7 +19,8 @@ fn main() {
|
|||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(250));
|
||||||
|
|
||||||
// TODO: You must take an action before you update a shared value.
|
// TODO: You must take an action before you update a shared value.
|
||||||
status_shared.jobs_done += 1;
|
let mut job_status = status_shared.lock().unwrap();
|
||||||
|
job_status.jobs_done += 1;
|
||||||
});
|
});
|
||||||
handles.push(handle);
|
handles.push(handle);
|
||||||
}
|
}
|
||||||
@ -30,5 +31,5 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Print the value of `JobStatus.jobs_done`.
|
// TODO: Print the value of `JobStatus.jobs_done`.
|
||||||
println!("Jobs done: {}", todo!());
|
println!("Jobs done: {}", status.lock().unwrap().jobs_done);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,21 +17,25 @@ impl Queue {
|
|||||||
fn send_tx(q: Queue, tx: mpsc::Sender<u32>) {
|
fn send_tx(q: Queue, tx: mpsc::Sender<u32>) {
|
||||||
// TODO: We want to send `tx` to both threads. But currently, it is moved
|
// TODO: We want to send `tx` to both threads. But currently, it is moved
|
||||||
// into the first thread. How could you solve this problem?
|
// into the first thread. How could you solve this problem?
|
||||||
|
|
||||||
|
let tx1=tx.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
for val in q.first_half {
|
for val in q.first_half {
|
||||||
println!("Sending {val:?}");
|
println!("Sending {val:?}");
|
||||||
tx.send(val).unwrap();
|
tx1.send(val).unwrap();
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(250));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let tx2=tx.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
for val in q.second_half {
|
for val in q.second_half {
|
||||||
println!("Sending {val:?}");
|
println!("Sending {val:?}");
|
||||||
tx.send(val).unwrap();
|
tx2.send(val).unwrap();
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(250));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
@ -6,5 +6,5 @@ macro_rules! my_macro {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// TODO: Fix the macro call.
|
// TODO: Fix the macro call.
|
||||||
my_macro();
|
my_macro!();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
fn main() {
|
|
||||||
my_macro!();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Fix the compiler error by moving the whole definition of this macro.
|
// TODO: Fix the compiler error by moving the whole definition of this macro.
|
||||||
macro_rules! my_macro {
|
macro_rules! my_macro {
|
||||||
() => {
|
() => {
|
||||||
println!("Check out my macro!");
|
println!("Check out my macro!");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
my_macro!();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
// TODO: Fix the compiler error without taking the macro definition out of this
|
// TODO: Fix the compiler error without taking the macro definition out of this
|
||||||
// module.
|
// module.
|
||||||
|
#[macro_use]
|
||||||
mod macros {
|
mod macros {
|
||||||
macro_rules! my_macro {
|
macro_rules! my_macro {
|
||||||
() => {
|
() => {
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
macro_rules! my_macro {
|
macro_rules! my_macro {
|
||||||
() => {
|
() => {
|
||||||
println!("Check out my macro!");
|
println!("Check out my macro!");
|
||||||
}
|
};
|
||||||
($val:expr) => {
|
($val:expr) => {
|
||||||
println!("Look at this other macro: {}", $val);
|
println!("Look at this other macro: {}", $val);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,10 +3,11 @@
|
|||||||
//
|
//
|
||||||
// For these exercises, the code will fail to compile when there are Clippy
|
// For these exercises, the code will fail to compile when there are Clippy
|
||||||
// warnings. Check Clippy's suggestions from the output to solve the exercise.
|
// warnings. Check Clippy's suggestions from the output to solve the exercise.
|
||||||
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// TODO: Fix the Clippy lint in this line.
|
// TODO: Fix the Clippy lint in this line.
|
||||||
let pi = 3.14;
|
let pi = PI;
|
||||||
let radius: f32 = 5.0;
|
let radius: f32 = 5.0;
|
||||||
|
|
||||||
let area = pi * radius.powi(2);
|
let area = pi * radius.powi(2);
|
||||||
|
|||||||
@ -2,7 +2,7 @@ fn main() {
|
|||||||
let mut res = 42;
|
let mut res = 42;
|
||||||
let option = Some(12);
|
let option = Some(12);
|
||||||
// TODO: Fix the Clippy lint.
|
// TODO: Fix the Clippy lint.
|
||||||
for x in option {
|
if let Some(x)= option {
|
||||||
res += x;
|
res += x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,24 +4,25 @@
|
|||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[allow(unused_variables, unused_assignments)]
|
#[allow(unused_variables, unused_assignments)]
|
||||||
fn main() {
|
fn main() {
|
||||||
let my_option: Option<()> = None;
|
let my_option: Option<()> = Some(());
|
||||||
if my_option.is_none() {
|
if my_option.is_none() {
|
||||||
println!("{:?}", my_option.unwrap());
|
println!("{my_option :?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let my_arr = &[
|
let my_arr = &[
|
||||||
-1, -2, -3
|
-1, -2, -3,
|
||||||
-4, -5, -6
|
-4, -5, -6
|
||||||
];
|
];
|
||||||
println!("My array! Here it is: {my_arr:?}");
|
println!("My array! Here it is: {my_arr:?}");
|
||||||
|
|
||||||
let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5);
|
let my_empty_vec: () = vec![1, 2, 3, 4, 5].resize(0, 5);
|
||||||
println!("This Vec is empty, see? {my_empty_vec:?}");
|
println!("This Vec is empty, see? {my_empty_vec:?}");
|
||||||
|
|
||||||
let mut value_a = 45;
|
let mut value_a = 45;
|
||||||
let mut value_b = 66;
|
let mut value_b = 66;
|
||||||
|
let pivot=value_b;
|
||||||
// Let's swap these two!
|
// Let's swap these two!
|
||||||
value_a = value_b;
|
value_a = value_b;
|
||||||
value_b = value_a;
|
value_b = pivot;
|
||||||
println!("value a: {value_a}; value b: {value_b}");
|
println!("value a: {value_a}; value b: {value_b}");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,20 +5,22 @@
|
|||||||
// Obtain the number of bytes (not characters) in the given argument
|
// Obtain the number of bytes (not characters) in the given argument
|
||||||
// (`.len()` returns the number of bytes in a string).
|
// (`.len()` returns the number of bytes in a string).
|
||||||
// TODO: Add the `AsRef` trait appropriately as a trait bound.
|
// TODO: Add the `AsRef` trait appropriately as a trait bound.
|
||||||
fn byte_counter<T>(arg: T) -> usize {
|
fn byte_counter<T:AsRef<str>>(arg: T) -> usize {
|
||||||
arg.as_ref().len()
|
arg.as_ref().len()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain the number of characters (not bytes) in the given argument.
|
// Obtain the number of characters (not bytes) in the given argument.
|
||||||
// TODO: Add the `AsRef` trait appropriately as a trait bound.
|
// TODO: Add the `AsRef` trait appropriately as a trait bound.
|
||||||
fn char_counter<T>(arg: T) -> usize {
|
fn char_counter<T:AsRef<str>>(arg: T) -> usize {
|
||||||
arg.as_ref().chars().count()
|
arg.as_ref().chars().count()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Squares a number using `as_mut()`.
|
// Squares a number using `as_mut()`.
|
||||||
// TODO: Add the appropriate trait bound.
|
// TODO: Add the appropriate trait bound.
|
||||||
fn num_sq<T>(arg: &mut T) {
|
fn num_sq<T:AsMut<u32>>(arg: &mut T) {
|
||||||
// TODO: Implement the function body.
|
// TODO: Implement the function body.
|
||||||
|
let v = arg.as_mut();
|
||||||
|
*v=v.pow(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
@ -34,7 +34,27 @@ impl Default for Person {
|
|||||||
// 5. Parse the second element from the split operation into a `u8` as the age.
|
// 5. Parse the second element from the split operation into a `u8` as the age.
|
||||||
// 6. If parsing the age fails, return the default of `Person`.
|
// 6. If parsing the age fails, return the default of `Person`.
|
||||||
impl From<&str> for Person {
|
impl From<&str> for Person {
|
||||||
fn from(s: &str) -> Self {}
|
fn from(s: &str) -> Self {
|
||||||
|
let split: Vec<&str> = s.split(",").collect();
|
||||||
|
|
||||||
|
if split.len() != 2 {
|
||||||
|
return Self::default();
|
||||||
|
}
|
||||||
|
let name = split[0];
|
||||||
|
let age_str = split[1];
|
||||||
|
|
||||||
|
if name.is_empty() {
|
||||||
|
return Self::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
match age_str.parse::<u8>() {
|
||||||
|
Ok(age) => Person {
|
||||||
|
name: name.to_string(),
|
||||||
|
age,
|
||||||
|
},
|
||||||
|
Err(_) => Self::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
@ -41,7 +41,27 @@ enum ParsePersonError {
|
|||||||
impl FromStr for Person {
|
impl FromStr for Person {
|
||||||
type Err = ParsePersonError;
|
type Err = ParsePersonError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {}
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let split: Vec<&str> = s.split(",").collect();
|
||||||
|
|
||||||
|
if split.len() != 2 {
|
||||||
|
return Err(ParsePersonError::BadLen);
|
||||||
|
}
|
||||||
|
let name = split[0];
|
||||||
|
let age_str = split[1];
|
||||||
|
|
||||||
|
if name.is_empty() {
|
||||||
|
return Err(ParsePersonError::NoName);
|
||||||
|
}
|
||||||
|
|
||||||
|
match age_str.parse::<u8>() {
|
||||||
|
Ok(age) => Ok(Person {
|
||||||
|
name: name.to_string(),
|
||||||
|
age,
|
||||||
|
}),
|
||||||
|
Err(e) => Err(ParsePersonError::ParseInt(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
@ -28,14 +28,35 @@ enum IntoColorError {
|
|||||||
impl TryFrom<(i16, i16, i16)> for Color {
|
impl TryFrom<(i16, i16, i16)> for Color {
|
||||||
type Error = IntoColorError;
|
type Error = IntoColorError;
|
||||||
|
|
||||||
fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {}
|
fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
|
||||||
|
let (a,b,c)=tuple;
|
||||||
|
if (0..=255).contains(&a) && (0..=255).contains(&b) && (0..=255).contains(&c) {
|
||||||
|
Ok(Color {
|
||||||
|
red: a as u8,
|
||||||
|
green: b as u8,
|
||||||
|
blue: c as u8,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(IntoColorError::IntConversion)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Array implementation.
|
// TODO: Array implementation.
|
||||||
impl TryFrom<[i16; 3]> for Color {
|
impl TryFrom<[i16; 3]> for Color {
|
||||||
type Error = IntoColorError;
|
type Error = IntoColorError;
|
||||||
|
|
||||||
fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {}
|
fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {
|
||||||
|
if arr.iter().all(|num| (0..=255).contains(num)){
|
||||||
|
Ok(Color {
|
||||||
|
red: arr[0] as u8,
|
||||||
|
green: arr[1] as u8,
|
||||||
|
blue: arr[2] as u8,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(IntoColorError::IntConversion)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Slice implementation.
|
// TODO: Slice implementation.
|
||||||
@ -43,7 +64,21 @@ impl TryFrom<[i16; 3]> for Color {
|
|||||||
impl TryFrom<&[i16]> for Color {
|
impl TryFrom<&[i16]> for Color {
|
||||||
type Error = IntoColorError;
|
type Error = IntoColorError;
|
||||||
|
|
||||||
fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {}
|
fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {
|
||||||
|
if slice.len()!=3{
|
||||||
|
return Err(IntoColorError::BadLen);
|
||||||
|
};
|
||||||
|
|
||||||
|
if slice.iter().all(|num| (0..=255).contains(num)) {
|
||||||
|
Ok(Color {
|
||||||
|
red: slice[0] as u8,
|
||||||
|
green: slice[1] as u8,
|
||||||
|
blue: slice[2] as u8,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(IntoColorError::IntConversion)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
fn average(values: &[f64]) -> f64 {
|
fn average(values: &[f64]) -> f64 {
|
||||||
let total = values.iter().sum::<f64>();
|
let total = values.iter().sum::<f64>();
|
||||||
// TODO: Make a conversion before dividing.
|
// TODO: Make a conversion before dividing.
|
||||||
total / values.len()
|
total / values.len() as f64
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|

|
||||||
# Exercise to Book Chapter mapping
|
# Exercise to Book Chapter mapping
|
||||||
|
|
||||||
| Exercise | Book Chapter |
|
| Exercise | Book Chapter |
|
||||||
|
|||||||
BIN
exercises/image.png
Normal file
BIN
exercises/image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@ -1,4 +1,45 @@
|
|||||||
|
// In this exercise, we are given a `Vec` of `u32` called `numbers` with values
|
||||||
|
// ranging from 0 to 99. We would like to use this set of numbers within 8
|
||||||
|
// different threads simultaneously. Each thread is going to get the sum of
|
||||||
|
// every eighth value with an offset.
|
||||||
|
//
|
||||||
|
// The first thread (offset 0), will sum 0, 8, 16, …
|
||||||
|
// The second thread (offset 1), will sum 1, 9, 17, …
|
||||||
|
// The third thread (offset 2), will sum 2, 10, 18, …
|
||||||
|
// …
|
||||||
|
// The eighth thread (offset 7), will sum 7, 15, 23, …
|
||||||
|
//
|
||||||
|
// Each thread should own a reference-counting pointer to the vector of
|
||||||
|
// numbers. But `Rc` isn't thread-safe. Therefore, we need to use `Arc`.
|
||||||
|
//
|
||||||
|
// Don't get distracted by how threads are spawned and joined. We will practice
|
||||||
|
// that later in the exercises about threads.
|
||||||
|
|
||||||
|
// Don't change the lines below.
|
||||||
|
#![forbid(unused_imports)]
|
||||||
|
use std::{sync::Arc, thread};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
let numbers: Vec<_> = (0..100u32).collect();
|
||||||
// It will be automatically filled after you finish the exercise.
|
|
||||||
|
let shared_numbers = Arc::new(numbers);
|
||||||
|
// ^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
let mut join_handles = Vec::new();
|
||||||
|
|
||||||
|
for offset in 0..8 {
|
||||||
|
let child_numbers = Arc::clone(&shared_numbers);
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
let handle = thread::spawn(move || {
|
||||||
|
let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum();
|
||||||
|
println!("Sum of offset {offset} is {sum}");
|
||||||
|
});
|
||||||
|
|
||||||
|
join_handles.push(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
for handle in join_handles.into_iter() {
|
||||||
|
handle.join().unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,47 @@
|
|||||||
fn main() {
|
// At compile time, Rust needs to know how much space a type takes up. This
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// becomes problematic for recursive types, where a value can have as part of
|
||||||
// It will be automatically filled after you finish the exercise.
|
// itself another value of the same type. To get around the issue, we can use a
|
||||||
|
// `Box` - a smart pointer used to store data on the heap, which also allows us
|
||||||
|
// to wrap a recursive type.
|
||||||
|
//
|
||||||
|
// The recursive type we're implementing in this exercise is the "cons list", a
|
||||||
|
// data structure frequently found in functional programming languages. Each
|
||||||
|
// item in a cons list contains two elements: The value of the current item and
|
||||||
|
// the next item. The last item is a value called `Nil`.
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
enum List {
|
||||||
|
Cons(i32, Box<List>),
|
||||||
|
Nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_empty_list() -> List {
|
||||||
|
List::Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_non_empty_list() -> List {
|
||||||
|
List::Cons(42, Box::new(List::Nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("This is an empty cons list: {:?}", create_empty_list());
|
||||||
|
println!(
|
||||||
|
"This is a non-empty cons list: {:?}",
|
||||||
|
create_non_empty_list(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_empty_list() {
|
||||||
|
assert_eq!(create_empty_list(), List::Nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_non_empty_list() {
|
||||||
|
assert_ne!(create_empty_list(), create_non_empty_list());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,69 @@
|
|||||||
fn main() {
|
// This exercise explores the `Cow` (Clone-On-Write) smart pointer. It can
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// enclose and provide immutable access to borrowed data and clone the data
|
||||||
// It will be automatically filled after you finish the exercise.
|
// lazily when mutation or ownership is required. The type is designed to work
|
||||||
|
// with general borrowed data via the `Borrow` trait.
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
fn abs_all(input: &mut Cow<[i32]>) {
|
||||||
|
for ind in 0..input.len() {
|
||||||
|
let value = input[ind];
|
||||||
|
if value < 0 {
|
||||||
|
// Clones into a vector if not already owned.
|
||||||
|
input.to_mut()[ind] = -value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reference_mutation() {
|
||||||
|
// Clone occurs because `input` needs to be mutated.
|
||||||
|
let vec = vec![-1, 0, 1];
|
||||||
|
let mut input = Cow::from(&vec);
|
||||||
|
abs_all(&mut input);
|
||||||
|
assert!(matches!(input, Cow::Owned(_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reference_no_mutation() {
|
||||||
|
// No clone occurs because `input` doesn't need to be mutated.
|
||||||
|
let vec = vec![0, 1, 2];
|
||||||
|
let mut input = Cow::from(&vec);
|
||||||
|
abs_all(&mut input);
|
||||||
|
assert!(matches!(input, Cow::Borrowed(_)));
|
||||||
|
// ^^^^^^^^^^^^^^^^
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn owned_no_mutation() {
|
||||||
|
// We can also pass `vec` without `&` so `Cow` owns it directly. In this
|
||||||
|
// case, no mutation occurs (all numbers are already absolute) and thus
|
||||||
|
// also no clone. But the result is still owned because it was never
|
||||||
|
// borrowed or mutated.
|
||||||
|
let vec = vec![0, 1, 2];
|
||||||
|
let mut input = Cow::from(vec);
|
||||||
|
abs_all(&mut input);
|
||||||
|
assert!(matches!(input, Cow::Owned(_)));
|
||||||
|
// ^^^^^^^^^^^^^
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn owned_mutation() {
|
||||||
|
// Of course this is also the case if a mutation does occur (not all
|
||||||
|
// numbers are absolute). In this case, the call to `to_mut()` in the
|
||||||
|
// `abs_all` function returns a reference to the same data as before.
|
||||||
|
let vec = vec![-1, 0, 1];
|
||||||
|
let mut input = Cow::from(vec);
|
||||||
|
abs_all(&mut input);
|
||||||
|
assert!(matches!(input, Cow::Owned(_)));
|
||||||
|
// ^^^^^^^^^^^^^
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,104 @@
|
|||||||
fn main() {
|
// In this exercise, we want to express the concept of multiple owners via the
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// `Rc<T>` type. This is a model of our solar system - there is a `Sun` type and
|
||||||
// It will be automatically filled after you finish the exercise.
|
// multiple `Planet`s. The planets take ownership of the sun, indicating that
|
||||||
|
// they revolve around the sun.
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Sun;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Planet {
|
||||||
|
Mercury(Rc<Sun>),
|
||||||
|
Venus(Rc<Sun>),
|
||||||
|
Earth(Rc<Sun>),
|
||||||
|
Mars(Rc<Sun>),
|
||||||
|
Jupiter(Rc<Sun>),
|
||||||
|
Saturn(Rc<Sun>),
|
||||||
|
Uranus(Rc<Sun>),
|
||||||
|
Neptune(Rc<Sun>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Planet {
|
||||||
|
fn details(&self) {
|
||||||
|
println!("Hi from {self:?}!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rc1() {
|
||||||
|
let sun = Rc::new(Sun);
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference
|
||||||
|
|
||||||
|
let mercury = Planet::Mercury(Rc::clone(&sun));
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 2 references
|
||||||
|
mercury.details();
|
||||||
|
|
||||||
|
let venus = Planet::Venus(Rc::clone(&sun));
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 3 references
|
||||||
|
venus.details();
|
||||||
|
|
||||||
|
let earth = Planet::Earth(Rc::clone(&sun));
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 4 references
|
||||||
|
earth.details();
|
||||||
|
|
||||||
|
let mars = Planet::Mars(Rc::clone(&sun));
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 5 references
|
||||||
|
mars.details();
|
||||||
|
|
||||||
|
let jupiter = Planet::Jupiter(Rc::clone(&sun));
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 6 references
|
||||||
|
jupiter.details();
|
||||||
|
|
||||||
|
let saturn = Planet::Saturn(Rc::clone(&sun));
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
|
||||||
|
saturn.details();
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
let uranus = Planet::Uranus(Rc::clone(&sun));
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
|
||||||
|
uranus.details();
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
let neptune = Planet::Neptune(Rc::clone(&sun));
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 9 references
|
||||||
|
neptune.details();
|
||||||
|
|
||||||
|
assert_eq!(Rc::strong_count(&sun), 9);
|
||||||
|
|
||||||
|
drop(neptune);
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
|
||||||
|
|
||||||
|
drop(uranus);
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
|
||||||
|
|
||||||
|
drop(saturn);
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 6 references
|
||||||
|
|
||||||
|
drop(jupiter);
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 5 references
|
||||||
|
|
||||||
|
drop(mars);
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 4 references
|
||||||
|
|
||||||
|
drop(earth);
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 3 references
|
||||||
|
|
||||||
|
drop(venus);
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 2 references
|
||||||
|
|
||||||
|
drop(mercury);
|
||||||
|
println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference
|
||||||
|
|
||||||
|
assert_eq!(Rc::strong_count(&sun), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,37 @@
|
|||||||
|
// This program spawns multiple threads that each runs for at least 250ms, and
|
||||||
|
// each thread returns how much time it took to complete. The program should
|
||||||
|
// wait until all the spawned threads have finished and should collect their
|
||||||
|
// return values into a vector.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
thread,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
let mut handles = Vec::new();
|
||||||
// It will be automatically filled after you finish the exercise.
|
for i in 0..10 {
|
||||||
|
let handle = thread::spawn(move || {
|
||||||
|
let start = Instant::now();
|
||||||
|
thread::sleep(Duration::from_millis(250));
|
||||||
|
println!("Thread {i} done");
|
||||||
|
start.elapsed().as_millis()
|
||||||
|
});
|
||||||
|
handles.push(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut results = Vec::new();
|
||||||
|
for handle in handles {
|
||||||
|
// Collect the results of all threads into the `results` vector.
|
||||||
|
results.push(handle.join().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
if results.len() != 10 {
|
||||||
|
panic!("Oh no! Some thread isn't done yet!");
|
||||||
|
}
|
||||||
|
|
||||||
|
println!();
|
||||||
|
for (i, result) in results.into_iter().enumerate() {
|
||||||
|
println!("Thread {i} took {result}ms");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,41 @@
|
|||||||
fn main() {
|
// Building on the last exercise, we want all of the threads to complete their
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// work. But this time, the spawned threads need to be in charge of updating a
|
||||||
// It will be automatically filled after you finish the exercise.
|
// shared value: `JobStatus.jobs_done`
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
thread,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JobStatus {
|
||||||
|
jobs_done: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// `Arc` isn't enough if you want a **mutable** shared state.
|
||||||
|
// We need to wrap the value with a `Mutex`.
|
||||||
|
let status = Arc::new(Mutex::new(JobStatus { jobs_done: 0 }));
|
||||||
|
// ^^^^^^^^^^^ ^
|
||||||
|
|
||||||
|
let mut handles = Vec::new();
|
||||||
|
for _ in 0..10 {
|
||||||
|
let status_shared = Arc::clone(&status);
|
||||||
|
let handle = thread::spawn(move || {
|
||||||
|
thread::sleep(Duration::from_millis(250));
|
||||||
|
|
||||||
|
// Lock before you update a shared value.
|
||||||
|
status_shared.lock().unwrap().jobs_done += 1;
|
||||||
|
// ^^^^^^^^^^^^^^^^
|
||||||
|
});
|
||||||
|
handles.push(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Waiting for all jobs to complete.
|
||||||
|
for handle in handles {
|
||||||
|
handle.join().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Jobs done: {}", status.lock().unwrap().jobs_done);
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,62 @@
|
|||||||
fn main() {
|
use std::{sync::mpsc, thread, time::Duration};
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
|
||||||
// It will be automatically filled after you finish the exercise.
|
struct Queue {
|
||||||
|
first_half: Vec<u32>,
|
||||||
|
second_half: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Queue {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
first_half: vec![1, 2, 3, 4, 5],
|
||||||
|
second_half: vec![6, 7, 8, 9, 10],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_tx(q: Queue, tx: mpsc::Sender<u32>) {
|
||||||
|
// Clone the sender `tx` first.
|
||||||
|
let tx_clone = tx.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
for val in q.first_half {
|
||||||
|
println!("Sending {val:?}");
|
||||||
|
// Then use the clone in the first thread. This means that
|
||||||
|
// `tx_clone` is moved to the first thread and `tx` to the second.
|
||||||
|
tx_clone.send(val).unwrap();
|
||||||
|
thread::sleep(Duration::from_millis(250));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
for val in q.second_half {
|
||||||
|
println!("Sending {val:?}");
|
||||||
|
tx.send(val).unwrap();
|
||||||
|
thread::sleep(Duration::from_millis(250));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn threads3() {
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
let queue = Queue::new();
|
||||||
|
|
||||||
|
send_tx(queue, tx);
|
||||||
|
|
||||||
|
let mut received = Vec::with_capacity(10);
|
||||||
|
for value in rx {
|
||||||
|
received.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
received.sort();
|
||||||
|
assert_eq!(received, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,10 @@
|
|||||||
fn main() {
|
macro_rules! my_macro {
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
() => {
|
||||||
// It will be automatically filled after you finish the exercise.
|
println!("Check out my macro!");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
my_macro!();
|
||||||
|
// ^
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,10 @@
|
|||||||
fn main() {
|
// Moved the macro definition to be before its call.
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
macro_rules! my_macro {
|
||||||
// It will be automatically filled after you finish the exercise.
|
() => {
|
||||||
|
println!("Check out my macro!");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
my_macro!();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,13 @@
|
|||||||
fn main() {
|
// Added the attribute `macro_use` attribute.
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
#[macro_use]
|
||||||
// It will be automatically filled after you finish the exercise.
|
mod macros {
|
||||||
|
macro_rules! my_macro {
|
||||||
|
() => {
|
||||||
|
println!("Check out my macro!");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
my_macro!();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,15 @@
|
|||||||
fn main() {
|
// Added semicolons to separate the macro arms.
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
#[rustfmt::skip]
|
||||||
// It will be automatically filled after you finish the exercise.
|
macro_rules! my_macro {
|
||||||
|
() => {
|
||||||
|
println!("Check out my macro!");
|
||||||
|
};
|
||||||
|
($val:expr) => {
|
||||||
|
println!("Look at this other macro: {}", $val);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
my_macro!();
|
||||||
|
my_macro!(7777);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,17 @@
|
|||||||
|
// The Clippy tool is a collection of lints to analyze your code so you can
|
||||||
|
// catch common mistakes and improve your Rust code.
|
||||||
|
//
|
||||||
|
// For these exercises, the code will fail to compile when there are Clippy
|
||||||
|
// warnings. Check Clippy's suggestions from the output to solve the exercise.
|
||||||
|
|
||||||
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// Use the more accurate `PI` constant.
|
||||||
// It will be automatically filled after you finish the exercise.
|
let pi = PI;
|
||||||
|
let radius: f32 = 5.0;
|
||||||
|
|
||||||
|
let area = pi * radius.powi(2);
|
||||||
|
|
||||||
|
println!("The area of a circle with radius {radius:.2} is {area:.5}");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,10 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
let mut res = 42;
|
||||||
// It will be automatically filled after you finish the exercise.
|
let option = Some(12);
|
||||||
|
// Use `if-let` instead of iteration.
|
||||||
|
if let Some(x) = option {
|
||||||
|
res += x;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{res}");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,31 @@
|
|||||||
|
use std::mem;
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[allow(unused_variables, unused_assignments)]
|
||||||
fn main() {
|
fn main() {
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
let my_option: Option<()> = None;
|
||||||
// It will be automatically filled after you finish the exercise.
|
// `unwrap` of an `Option` after checking if it is `None` will panic.
|
||||||
|
// Use `if-let` instead.
|
||||||
|
if let Some(value) = my_option {
|
||||||
|
println!("{value:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// A comma was missing.
|
||||||
|
let my_arr = &[
|
||||||
|
-1, -2, -3,
|
||||||
|
-4, -5, -6,
|
||||||
|
];
|
||||||
|
println!("My array! Here it is: {:?}", my_arr);
|
||||||
|
|
||||||
|
let mut my_empty_vec = vec![1, 2, 3, 4, 5];
|
||||||
|
// `resize` mutates a vector instead of returning a new one.
|
||||||
|
// `resize(0, …)` clears a vector, so it is better to use `clear`.
|
||||||
|
my_empty_vec.clear();
|
||||||
|
println!("This Vec is empty, see? {my_empty_vec:?}");
|
||||||
|
|
||||||
|
let mut value_a = 45;
|
||||||
|
let mut value_b = 66;
|
||||||
|
// Use `mem::swap` to correctly swap two values.
|
||||||
|
mem::swap(&mut value_a, &mut value_b);
|
||||||
|
println!("value a: {}; value b: {}", value_a, value_b);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,60 @@
|
|||||||
fn main() {
|
// AsRef and AsMut allow for cheap reference-to-reference conversions. Read more
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html and
|
||||||
// It will be automatically filled after you finish the exercise.
|
// https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively.
|
||||||
|
|
||||||
|
// Obtain the number of bytes (not characters) in the given argument
|
||||||
|
// (`.len()` returns the number of bytes in a string).
|
||||||
|
fn byte_counter<T: AsRef<str>>(arg: T) -> usize {
|
||||||
|
arg.as_ref().len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain the number of characters (not bytes) in the given argument.
|
||||||
|
fn char_counter<T: AsRef<str>>(arg: T) -> usize {
|
||||||
|
arg.as_ref().chars().count()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Squares a number using `as_mut()`.
|
||||||
|
fn num_sq<T: AsMut<u32>>(arg: &mut T) {
|
||||||
|
let arg = arg.as_mut();
|
||||||
|
*arg *= *arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn different_counts() {
|
||||||
|
let s = "Café au lait";
|
||||||
|
assert_ne!(char_counter(s), byte_counter(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn same_counts() {
|
||||||
|
let s = "Cafe au lait";
|
||||||
|
assert_eq!(char_counter(s), byte_counter(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn different_counts_using_string() {
|
||||||
|
let s = String::from("Café au lait");
|
||||||
|
assert_ne!(char_counter(s.clone()), byte_counter(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn same_counts_using_string() {
|
||||||
|
let s = String::from("Cafe au lait");
|
||||||
|
assert_eq!(char_counter(s.clone()), byte_counter(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mut_box() {
|
||||||
|
let mut num: Box<u32> = Box::new(3);
|
||||||
|
num_sq(&mut num);
|
||||||
|
assert_eq!(*num, 9);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,136 @@
|
|||||||
fn main() {
|
// The `From` trait is used for value-to-value conversions. If `From` is
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// implemented, an implementation of `Into` is automatically provided.
|
||||||
// It will be automatically filled after you finish the exercise.
|
// You can read more about it in the documentation:
|
||||||
|
// https://doc.rust-lang.org/std/convert/trait.From.html
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Person {
|
||||||
|
name: String,
|
||||||
|
age: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// We implement the Default trait to use it as a fallback when the provided
|
||||||
|
// string is not convertible into a `Person` object.
|
||||||
|
impl Default for Person {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
name: String::from("John"),
|
||||||
|
age: 30,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Person {
|
||||||
|
fn from(s: &str) -> Self {
|
||||||
|
let mut split = s.split(',');
|
||||||
|
let (Some(name), Some(age), None) = (split.next(), split.next(), split.next()) else {
|
||||||
|
// ^^^^ there should be no third element
|
||||||
|
return Self::default();
|
||||||
|
};
|
||||||
|
|
||||||
|
if name.is_empty() {
|
||||||
|
return Self::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
let Ok(age) = age.parse() else {
|
||||||
|
return Self::default();
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
name: name.into(),
|
||||||
|
age,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Use the `from` function.
|
||||||
|
let p1 = Person::from("Mark,20");
|
||||||
|
println!("{p1:?}");
|
||||||
|
|
||||||
|
// Since `From` is implemented for Person, we are able to use `Into`.
|
||||||
|
let p2: Person = "Gerald,70".into();
|
||||||
|
println!("{p2:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_default() {
|
||||||
|
let dp = Person::default();
|
||||||
|
assert_eq!(dp.name, "John");
|
||||||
|
assert_eq!(dp.age, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bad_convert() {
|
||||||
|
let p = Person::from("");
|
||||||
|
assert_eq!(p.name, "John");
|
||||||
|
assert_eq!(p.age, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_good_convert() {
|
||||||
|
let p = Person::from("Mark,20");
|
||||||
|
assert_eq!(p.name, "Mark");
|
||||||
|
assert_eq!(p.age, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bad_age() {
|
||||||
|
let p = Person::from("Mark,twenty");
|
||||||
|
assert_eq!(p.name, "John");
|
||||||
|
assert_eq!(p.age, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_missing_comma_and_age() {
|
||||||
|
let p: Person = Person::from("Mark");
|
||||||
|
assert_eq!(p.name, "John");
|
||||||
|
assert_eq!(p.age, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_missing_age() {
|
||||||
|
let p: Person = Person::from("Mark,");
|
||||||
|
assert_eq!(p.name, "John");
|
||||||
|
assert_eq!(p.age, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_missing_name() {
|
||||||
|
let p: Person = Person::from(",1");
|
||||||
|
assert_eq!(p.name, "John");
|
||||||
|
assert_eq!(p.age, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_missing_name_and_age() {
|
||||||
|
let p: Person = Person::from(",");
|
||||||
|
assert_eq!(p.name, "John");
|
||||||
|
assert_eq!(p.age, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_missing_name_and_invalid_age() {
|
||||||
|
let p: Person = Person::from(",one");
|
||||||
|
assert_eq!(p.name, "John");
|
||||||
|
assert_eq!(p.age, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trailing_comma() {
|
||||||
|
let p: Person = Person::from("Mike,32,");
|
||||||
|
assert_eq!(p.name, "John");
|
||||||
|
assert_eq!(p.age, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trailing_comma_and_some_string() {
|
||||||
|
let p: Person = Person::from("Mike,32,dog");
|
||||||
|
assert_eq!(p.name, "John");
|
||||||
|
assert_eq!(p.age, 30);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,117 @@
|
|||||||
fn main() {
|
// This is similar to the previous `from_into` exercise. But this time, we'll
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// implement `FromStr` and return errors instead of falling back to a default
|
||||||
// It will be automatically filled after you finish the exercise.
|
// value. Additionally, upon implementing `FromStr`, you can use the `parse`
|
||||||
|
// method on strings to generate an object of the implementor type. You can read
|
||||||
|
// more about it in the documentation:
|
||||||
|
// https://doc.rust-lang.org/std/str/trait.FromStr.html
|
||||||
|
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
struct Person {
|
||||||
|
name: String,
|
||||||
|
age: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will use this error type for the `FromStr` implementation.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum ParsePersonError {
|
||||||
|
// Incorrect number of fields
|
||||||
|
BadLen,
|
||||||
|
// Empty name field
|
||||||
|
NoName,
|
||||||
|
// Wrapped error from parse::<u8>()
|
||||||
|
ParseInt(ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Person {
|
||||||
|
type Err = ParsePersonError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut split = s.split(',');
|
||||||
|
let (Some(name), Some(age), None) = (split.next(), split.next(), split.next()) else {
|
||||||
|
// ^^^^ there should be no third element
|
||||||
|
return Err(ParsePersonError::BadLen);
|
||||||
|
};
|
||||||
|
|
||||||
|
if name.is_empty() {
|
||||||
|
return Err(ParsePersonError::NoName);
|
||||||
|
}
|
||||||
|
|
||||||
|
let age = age.parse().map_err(ParsePersonError::ParseInt)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
name: name.into(),
|
||||||
|
age,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let p = "Mark,20".parse::<Person>();
|
||||||
|
println!("{p:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use ParsePersonError::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_input() {
|
||||||
|
assert_eq!("".parse::<Person>(), Err(BadLen));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn good_input() {
|
||||||
|
let p = "John,32".parse::<Person>();
|
||||||
|
assert!(p.is_ok());
|
||||||
|
let p = p.unwrap();
|
||||||
|
assert_eq!(p.name, "John");
|
||||||
|
assert_eq!(p.age, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_age() {
|
||||||
|
assert!(matches!("John,".parse::<Person>(), Err(ParseInt(_))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_age() {
|
||||||
|
assert!(matches!("John,twenty".parse::<Person>(), Err(ParseInt(_))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_comma_and_age() {
|
||||||
|
assert_eq!("John".parse::<Person>(), Err(BadLen));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_name() {
|
||||||
|
assert_eq!(",1".parse::<Person>(), Err(NoName));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_name_and_age() {
|
||||||
|
assert!(matches!(",".parse::<Person>(), Err(NoName | ParseInt(_))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_name_and_invalid_age() {
|
||||||
|
assert!(matches!(
|
||||||
|
",one".parse::<Person>(),
|
||||||
|
Err(NoName | ParseInt(_)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trailing_comma() {
|
||||||
|
assert_eq!("John,32,".parse::<Person>(), Err(BadLen));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trailing_comma_and_some_string() {
|
||||||
|
assert_eq!("John,32,man".parse::<Person>(), Err(BadLen));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,193 @@
|
|||||||
fn main() {
|
// `TryFrom` is a simple and safe type conversion that may fail in a controlled
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// way under some circumstances. Basically, this is the same as `From`. The main
|
||||||
// It will be automatically filled after you finish the exercise.
|
// difference is that this should return a `Result` type instead of the target
|
||||||
|
// type itself. You can read more about it in the documentation:
|
||||||
|
// https://doc.rust-lang.org/std/convert/trait.TryFrom.html
|
||||||
|
|
||||||
|
#![allow(clippy::useless_vec)]
|
||||||
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
struct Color {
|
||||||
|
red: u8,
|
||||||
|
green: u8,
|
||||||
|
blue: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will use this error type for the `TryFrom` conversions.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum IntoColorError {
|
||||||
|
// Incorrect length of slice
|
||||||
|
BadLen,
|
||||||
|
// Integer conversion error
|
||||||
|
IntConversion,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<(i16, i16, i16)> for Color {
|
||||||
|
type Error = IntoColorError;
|
||||||
|
|
||||||
|
fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
|
||||||
|
let (Ok(red), Ok(green), Ok(blue)) = (
|
||||||
|
u8::try_from(tuple.0),
|
||||||
|
u8::try_from(tuple.1),
|
||||||
|
u8::try_from(tuple.2),
|
||||||
|
) else {
|
||||||
|
return Err(IntoColorError::IntConversion);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self { red, green, blue })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<[i16; 3]> for Color {
|
||||||
|
type Error = IntoColorError;
|
||||||
|
|
||||||
|
fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {
|
||||||
|
// Reuse the implementation for a tuple.
|
||||||
|
Self::try_from((arr[0], arr[1], arr[2]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[i16]> for Color {
|
||||||
|
type Error = IntoColorError;
|
||||||
|
|
||||||
|
fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {
|
||||||
|
// Check the length.
|
||||||
|
if slice.len() != 3 {
|
||||||
|
return Err(IntoColorError::BadLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reuse the implementation for a tuple.
|
||||||
|
Self::try_from((slice[0], slice[1], slice[2]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Using the `try_from` function.
|
||||||
|
let c1 = Color::try_from((183, 65, 14));
|
||||||
|
println!("{c1:?}");
|
||||||
|
|
||||||
|
// Since `TryFrom` is implemented for `Color`, we can use `TryInto`.
|
||||||
|
let c2: Result<Color, _> = [183, 65, 14].try_into();
|
||||||
|
println!("{c2:?}");
|
||||||
|
|
||||||
|
let v = vec![183, 65, 14];
|
||||||
|
// With slice we should use the `try_from` function
|
||||||
|
let c3 = Color::try_from(&v[..]);
|
||||||
|
println!("{c3:?}");
|
||||||
|
// or put the slice within round brackets and use `try_into`.
|
||||||
|
let c4: Result<Color, _> = (&v[..]).try_into();
|
||||||
|
println!("{c4:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use IntoColorError::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tuple_out_of_range_positive() {
|
||||||
|
assert_eq!(Color::try_from((256, 1000, 10000)), Err(IntConversion));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tuple_out_of_range_negative() {
|
||||||
|
assert_eq!(Color::try_from((-1, -10, -256)), Err(IntConversion));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tuple_sum() {
|
||||||
|
assert_eq!(Color::try_from((-1, 255, 255)), Err(IntConversion));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tuple_correct() {
|
||||||
|
let c: Result<Color, _> = (183, 65, 14).try_into();
|
||||||
|
assert!(c.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
c.unwrap(),
|
||||||
|
Color {
|
||||||
|
red: 183,
|
||||||
|
green: 65,
|
||||||
|
blue: 14,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_array_out_of_range_positive() {
|
||||||
|
let c: Result<Color, _> = [1000, 10000, 256].try_into();
|
||||||
|
assert_eq!(c, Err(IntConversion));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_array_out_of_range_negative() {
|
||||||
|
let c: Result<Color, _> = [-10, -256, -1].try_into();
|
||||||
|
assert_eq!(c, Err(IntConversion));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_array_sum() {
|
||||||
|
let c: Result<Color, _> = [-1, 255, 255].try_into();
|
||||||
|
assert_eq!(c, Err(IntConversion));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_array_correct() {
|
||||||
|
let c: Result<Color, _> = [183, 65, 14].try_into();
|
||||||
|
assert!(c.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
c.unwrap(),
|
||||||
|
Color {
|
||||||
|
red: 183,
|
||||||
|
green: 65,
|
||||||
|
blue: 14
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_slice_out_of_range_positive() {
|
||||||
|
let arr = [10000, 256, 1000];
|
||||||
|
assert_eq!(Color::try_from(&arr[..]), Err(IntConversion));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_slice_out_of_range_negative() {
|
||||||
|
let arr = [-256, -1, -10];
|
||||||
|
assert_eq!(Color::try_from(&arr[..]), Err(IntConversion));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_slice_sum() {
|
||||||
|
let arr = [-1, 255, 255];
|
||||||
|
assert_eq!(Color::try_from(&arr[..]), Err(IntConversion));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_slice_correct() {
|
||||||
|
let v = vec![183, 65, 14];
|
||||||
|
let c: Result<Color, _> = Color::try_from(&v[..]);
|
||||||
|
assert!(c.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
c.unwrap(),
|
||||||
|
Color {
|
||||||
|
red: 183,
|
||||||
|
green: 65,
|
||||||
|
blue: 14,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_slice_excess_length() {
|
||||||
|
let v = vec![0, 0, 0, 0];
|
||||||
|
assert_eq!(Color::try_from(&v[..]), Err(BadLen));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_slice_insufficient_length() {
|
||||||
|
let v = vec![0, 0];
|
||||||
|
assert_eq!(Color::try_from(&v[..]), Err(BadLen));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,24 @@
|
|||||||
fn main() {
|
// Type casting in Rust is done via the usage of the `as` operator.
|
||||||
// DON'T EDIT THIS SOLUTION FILE!
|
// Note that the `as` operator is not only used when type casting. It also helps
|
||||||
// It will be automatically filled after you finish the exercise.
|
// with renaming imports.
|
||||||
|
|
||||||
|
fn average(values: &[f64]) -> f64 {
|
||||||
|
let total = values.iter().sum::<f64>();
|
||||||
|
total / values.len() as f64
|
||||||
|
// ^^^^^^
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let values = [3.5, 0.3, 13.0, 11.7];
|
||||||
|
println!("{}", average(&values));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn returns_proper_type_and_value() {
|
||||||
|
assert_eq!(average(&[3.5, 0.3, 13.0, 11.7]), 7.125);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user