mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-12-28 06:49:19 +00:00
23th completed
This commit is contained in:
parent
de07320d0c
commit
27860807eb
@ -1,6 +1,6 @@
|
||||
DON'T EDIT THIS FILE!
|
||||
|
||||
box1
|
||||
as_ref_mut
|
||||
|
||||
intro1
|
||||
intro2
|
||||
@ -77,3 +77,22 @@ iterators2
|
||||
iterators3
|
||||
iterators4
|
||||
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();
|
||||
|
||||
// TODO: Define `shared_numbers` by using `Arc`.
|
||||
// let shared_numbers = ???;
|
||||
let shared_numbers = Arc::new(numbers);
|
||||
|
||||
let mut join_handles = Vec::new();
|
||||
|
||||
for offset in 0..8 {
|
||||
// TODO: Define `child_numbers` using `shared_numbers`.
|
||||
// let child_numbers = ???;
|
||||
let child_numbers = Arc::clone(&shared_numbers);
|
||||
|
||||
let handle = thread::spawn(move || {
|
||||
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.
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum List {
|
||||
Cons(i32, List),
|
||||
Cons(i32, Box<List>),
|
||||
Nil,
|
||||
}
|
||||
|
||||
// TODO: Create an empty cons list.
|
||||
fn create_empty_list() -> List {
|
||||
todo!()
|
||||
List::Nil
|
||||
}
|
||||
|
||||
// TODO: Create a non-empty cons list.
|
||||
fn create_non_empty_list() -> List {
|
||||
todo!()
|
||||
List::Cons(1, Box::new(List::Nil))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
@ -39,7 +39,7 @@ mod tests {
|
||||
let mut input = Cow::from(&vec);
|
||||
abs_all(&mut input);
|
||||
// TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
|
||||
assert!(matches!(input, todo!()));
|
||||
assert!(matches!(input,Cow::Borrowed(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -52,7 +52,7 @@ mod tests {
|
||||
let mut input = Cow::from(vec);
|
||||
abs_all(&mut input);
|
||||
// TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.
|
||||
assert!(matches!(input, todo!()));
|
||||
assert!(matches!(input, Cow::Owned(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -64,6 +64,6 @@ mod tests {
|
||||
let mut input = Cow::from(vec);
|
||||
abs_all(&mut input);
|
||||
// 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();
|
||||
|
||||
// TODO
|
||||
let saturn = Planet::Saturn(Rc::new(Sun));
|
||||
let saturn = Planet::Saturn(Rc::clone(&sun));
|
||||
println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
|
||||
saturn.details();
|
||||
|
||||
// TODO
|
||||
let uranus = Planet::Uranus(Rc::new(Sun));
|
||||
let uranus = Planet::Uranus(Rc::clone(&sun));
|
||||
println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
|
||||
uranus.details();
|
||||
|
||||
// TODO
|
||||
let neptune = Planet::Neptune(Rc::new(Sun));
|
||||
let neptune = Planet::Neptune(Rc::clone(&sun));
|
||||
println!("reference count = {}", Rc::strong_count(&sun)); // 9 references
|
||||
neptune.details();
|
||||
|
||||
@ -92,12 +92,17 @@ mod tests {
|
||||
println!("reference count = {}", Rc::strong_count(&sun)); // 4 references
|
||||
|
||||
// TODO
|
||||
drop(mercury);
|
||||
|
||||
println!("reference count = {}", Rc::strong_count(&sun)); // 3 references
|
||||
|
||||
// TODO
|
||||
drop(venus);
|
||||
|
||||
println!("reference count = {}", Rc::strong_count(&sun)); // 2 references
|
||||
|
||||
// TODO
|
||||
drop(earth);
|
||||
println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference
|
||||
|
||||
assert_eq!(Rc::strong_count(&sun), 1);
|
||||
|
||||
@ -24,6 +24,12 @@ fn main() {
|
||||
for handle in handles {
|
||||
// TODO: Collect the results of all threads into the `results` vector.
|
||||
// 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 {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// work. But this time, the spawned threads need to be in charge of updating a
|
||||
// shared value: `JobStatus.jobs_done`
|
||||
|
||||
use std::{sync::Arc, thread, time::Duration};
|
||||
use std::{sync::{Arc, Mutex}, thread, time::Duration};
|
||||
|
||||
struct JobStatus {
|
||||
jobs_done: u32,
|
||||
@ -10,7 +10,7 @@ struct JobStatus {
|
||||
|
||||
fn main() {
|
||||
// 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();
|
||||
for _ in 0..10 {
|
||||
@ -19,7 +19,8 @@ fn main() {
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
|
||||
// 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);
|
||||
}
|
||||
@ -30,5 +31,5 @@ fn main() {
|
||||
}
|
||||
|
||||
// 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>) {
|
||||
// TODO: We want to send `tx` to both threads. But currently, it is moved
|
||||
// into the first thread. How could you solve this problem?
|
||||
|
||||
let tx1=tx.clone();
|
||||
thread::spawn(move || {
|
||||
for val in q.first_half {
|
||||
println!("Sending {val:?}");
|
||||
tx.send(val).unwrap();
|
||||
tx1.send(val).unwrap();
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
}
|
||||
});
|
||||
|
||||
let tx2=tx.clone();
|
||||
thread::spawn(move || {
|
||||
for val in q.second_half {
|
||||
println!("Sending {val:?}");
|
||||
tx.send(val).unwrap();
|
||||
tx2.send(val).unwrap();
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
@ -6,5 +6,5 @@ macro_rules! my_macro {
|
||||
|
||||
fn main() {
|
||||
// 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.
|
||||
macro_rules! 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
|
||||
// module.
|
||||
#[macro_use]
|
||||
mod macros {
|
||||
macro_rules! my_macro {
|
||||
() => {
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
macro_rules! my_macro {
|
||||
() => {
|
||||
println!("Check out my macro!");
|
||||
}
|
||||
};
|
||||
($val:expr) => {
|
||||
println!("Look at this other macro: {}", $val);
|
||||
}
|
||||
|
||||
@ -3,10 +3,11 @@
|
||||
//
|
||||
// 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() {
|
||||
// TODO: Fix the Clippy lint in this line.
|
||||
let pi = 3.14;
|
||||
let pi = PI;
|
||||
let radius: f32 = 5.0;
|
||||
|
||||
let area = pi * radius.powi(2);
|
||||
|
||||
@ -2,7 +2,7 @@ fn main() {
|
||||
let mut res = 42;
|
||||
let option = Some(12);
|
||||
// TODO: Fix the Clippy lint.
|
||||
for x in option {
|
||||
if let Some(x)= option {
|
||||
res += x;
|
||||
}
|
||||
|
||||
|
||||
@ -4,24 +4,25 @@
|
||||
#[rustfmt::skip]
|
||||
#[allow(unused_variables, unused_assignments)]
|
||||
fn main() {
|
||||
let my_option: Option<()> = None;
|
||||
let my_option: Option<()> = Some(());
|
||||
if my_option.is_none() {
|
||||
println!("{:?}", my_option.unwrap());
|
||||
println!("{my_option :?}");
|
||||
}
|
||||
|
||||
let my_arr = &[
|
||||
-1, -2, -3
|
||||
-1, -2, -3,
|
||||
-4, -5, -6
|
||||
];
|
||||
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:?}");
|
||||
|
||||
let mut value_a = 45;
|
||||
let mut value_b = 66;
|
||||
let pivot=value_b;
|
||||
// Let's swap these two!
|
||||
value_a = value_b;
|
||||
value_b = value_a;
|
||||
value_b = pivot;
|
||||
println!("value a: {value_a}; value b: {value_b}");
|
||||
}
|
||||
|
||||
@ -5,20 +5,22 @@
|
||||
// Obtain the number of bytes (not characters) in the given argument
|
||||
// (`.len()` returns the number of bytes in a string).
|
||||
// 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()
|
||||
}
|
||||
|
||||
// Obtain the number of characters (not bytes) in the given argument.
|
||||
// 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()
|
||||
}
|
||||
|
||||
// Squares a number using `as_mut()`.
|
||||
// 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.
|
||||
let v = arg.as_mut();
|
||||
*v=v.pow(2);
|
||||
}
|
||||
|
||||
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.
|
||||
// 6. If parsing the age fails, return the default of `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() {
|
||||
|
||||
@ -41,7 +41,27 @@ enum ParsePersonError {
|
||||
impl FromStr for Person {
|
||||
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() {
|
||||
|
||||
@ -28,14 +28,35 @@ enum IntoColorError {
|
||||
impl TryFrom<(i16, i16, i16)> for Color {
|
||||
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.
|
||||
impl TryFrom<[i16; 3]> for Color {
|
||||
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.
|
||||
@ -43,7 +64,21 @@ impl TryFrom<[i16; 3]> for Color {
|
||||
impl TryFrom<&[i16]> for Color {
|
||||
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() {
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
fn average(values: &[f64]) -> f64 {
|
||||
let total = values.iter().sum::<f64>();
|
||||
// TODO: Make a conversion before dividing.
|
||||
total / values.len()
|
||||
total / values.len() as f64
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||

|
||||
# Exercise to Book Chapter mapping
|
||||
|
||||
| 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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
let numbers: Vec<_> = (0..100u32).collect();
|
||||
|
||||
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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// At compile time, Rust needs to know how much space a type takes up. This
|
||||
// becomes problematic for recursive types, where a value can have as part of
|
||||
// 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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// This exercise explores the `Cow` (Clone-On-Write) smart pointer. It can
|
||||
// enclose and provide immutable access to borrowed data and clone the data
|
||||
// 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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// In this exercise, we want to express the concept of multiple owners via the
|
||||
// `Rc<T>` type. This is a model of our solar system - there is a `Sun` type and
|
||||
// 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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
let mut handles = Vec::new();
|
||||
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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// Building on the last exercise, we want all of the threads to complete their
|
||||
// work. But this time, the spawned threads need to be in charge of updating a
|
||||
// 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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
use std::{sync::mpsc, thread, time::Duration};
|
||||
|
||||
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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
macro_rules! my_macro {
|
||||
() => {
|
||||
println!("Check out my macro!");
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
my_macro!();
|
||||
// ^
|
||||
}
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
fn main() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// Moved the macro definition to be before its call.
|
||||
macro_rules! my_macro {
|
||||
() => {
|
||||
println!("Check out my macro!");
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
my_macro!();
|
||||
}
|
||||
|
||||
@ -1,4 +1,13 @@
|
||||
fn main() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// Added the attribute `macro_use` attribute.
|
||||
#[macro_use]
|
||||
mod macros {
|
||||
macro_rules! my_macro {
|
||||
() => {
|
||||
println!("Check out my macro!");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
my_macro!();
|
||||
}
|
||||
|
||||
@ -1,4 +1,15 @@
|
||||
fn main() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// Added semicolons to separate the macro arms.
|
||||
#[rustfmt::skip]
|
||||
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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// Use the more accurate `PI` constant.
|
||||
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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
let mut res = 42;
|
||||
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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
let my_option: Option<()> = None;
|
||||
// `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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// AsRef and AsMut allow for cheap reference-to-reference conversions. Read more
|
||||
// about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html and
|
||||
// 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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// The `From` trait is used for value-to-value conversions. If `From` is
|
||||
// implemented, an implementation of `Into` is automatically provided.
|
||||
// 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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// This is similar to the previous `from_into` exercise. But this time, we'll
|
||||
// implement `FromStr` and return errors instead of falling back to a default
|
||||
// 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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// `TryFrom` is a simple and safe type conversion that may fail in a controlled
|
||||
// way under some circumstances. Basically, this is the same as `From`. The main
|
||||
// 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() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
// Type casting in Rust is done via the usage of the `as` operator.
|
||||
// Note that the `as` operator is not only used when type casting. It also helps
|
||||
// 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