Esimerkkiratkaisut
01-impl
//! Your task is to add the `color` method to `TrafficLightColor`. #[derive(Debug)] enum TrafficLightColor { Red, Yellow, Green, } impl TrafficLightColor { pub fn color(&self) -> &str { match self { Self::Red => "red", Self::Yellow => "yellow", Self::Green => "green", } } } fn main() { let c = TrafficLightColor::Yellow; assert_eq!(c.color(), "yellow"); println!("{:?}", c); }
02-errors
#![allow(unused)] fn main() { //! Your task is to fix the `new` method. #[derive(PartialEq, Debug)] struct PositiveInteger(u64); #[derive(PartialEq, Debug)] enum CreationError { Negative, Zero, } impl PositiveInteger { /// Creates a [`PositiveInteger`] from an `i64`. /// /// # Errors /// /// - [`CreationError::Negative`] is returned if the `value` is negative. /// - [`CreationError::Zero`] is returned if the `value` is zero. fn new(value: i64) -> Result<PositiveInteger, CreationError> { if value < 0 { Err(CreationError::Negative) } else if value == 0 { Err(CreationError::Zero) } else { Ok(PositiveInteger(value as u64)) } } } #[test] fn test_creation() { assert!(PositiveInteger::new(10).is_ok()); assert_eq!(Err(CreationError::Negative), PositiveInteger::new(-10)); assert_eq!(Err(CreationError::Zero), PositiveInteger::new(0)); } }
03-errors2
#![allow(unused)] fn main() { //! Your task is to fix the return type of `main` and change the "user input" //! if you know what I mean... use std::error; use std::fmt; use std::num::ParseIntError; #[test] fn _main() -> Result<(), Box<dyn error::Error>> { let pretend_user_input = "42"; let x: i64 = pretend_user_input.parse()?; let s = PositiveNonzeroInteger::new(x)?; assert_eq!(PositiveNonzeroInteger(42), s); Ok(()) } // Don't change anything below this line. #[derive(PartialEq, Debug)] struct PositiveNonzeroInteger(u64); #[derive(PartialEq, Debug)] enum CreationError { Negative, Zero, } impl PositiveNonzeroInteger { fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> { match value { 0 => Err(CreationError::Zero), 1.. => Ok(PositiveNonzeroInteger(value as u64)), _ => Err(CreationError::Negative), } } } // This is required so that `CreationError` can implement `error::Error`. impl fmt::Display for CreationError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let description = match *self { CreationError::Negative => "number is negative", CreationError::Zero => "number is zero", }; f.write_str(description) } } impl error::Error for CreationError {} }
04-monad
#![allow(unused)] fn main() { //! Your task is to rewrite `bad_multiply` without using `match` or `if left`. //! Remember that `Option`s are monads, and monads are just monoids in the //! category of endofunctors. Big deal ¯\_(ツ)¯\_ use std::num::ParseIntError; fn bad_multiply(left: &str, right: &str) -> Result<i32, ParseIntError> { match left.parse::<i32>() { Ok(n1) => match right.parse::<i32>() { Ok(n2) => Ok(n1 * n2), Err(e) => Err(e), }, Err(e) => Err(e), } } fn multiply(left: &str, right: &str) -> Result<i32, ParseIntError> { left.parse::<i32>() .and_then(|left| right.parse::<i32>().map(|right| left * right)) } #[test] fn _main() { // This still presents a reasonable answer. let twenty = multiply("10", "2"); assert_eq!(twenty, Ok(20)); // The following now provides a much more helpful error message. let not_twenty = multiply("ten", "2"); assert!(matches!(not_twenty, Err(ParseIntError))); } }
05-errors3
#![allow(unused)] fn main() { //! Your task is to fill in the blanks. Read the documentation comments. //! //! Using catch-all error types like `Box<dyn error::Error>` isn't recommended //! for library code, where callers might want to handle the errors. It is //! possible to downcast the trait object, but is more cumbersome than matching //! on an enum directly. use std::num::ParseIntError; #[derive(PartialEq, Debug)] enum Error { /// A [`CreationError`] from creating the [`PositiveInteger`]. Creation(CreationError), /// A [`ParseIntError`] from parsing the input string. ParseInt(ParseIntError), } /// Parses a string into a [`PositiveInteger`]. /// /// # Errors /// /// - [`Error::ParseInt`] is returned if parsing `s` into an integer fails. /// - [`Error::Creation`] is returned if creating the [`PositiveInteger`] fails. fn parse_positive(s: impl AsRef<str>) -> Result<PositiveInteger, Error> { // Tip: Error::ParseInt is also a function: ___ -> Error let x: i64 = s.as_ref().parse().map_err(Error::ParseInt)?; PositiveInteger::new(x).map_err(Error::Creation) } // Don't change anything below this line. #[derive(PartialEq, Debug)] struct PositiveInteger(u64); #[derive(PartialEq, Debug)] enum CreationError { Negative, Zero, } impl PositiveInteger { fn new(value: i64) -> Result<PositiveInteger, CreationError> { match value { 0 => Err(CreationError::Zero), 1.. => Ok(PositiveInteger(value as u64)), _ => Err(CreationError::Negative), } } } #[cfg(test)] mod test { use super::*; #[test] fn test_parse_error() { assert!(matches!( parse_positive("not a number"), Err(Error::ParseInt(_)) )); } #[test] fn test_negative() { assert_eq!( parse_positive("-555"), Err(Error::Creation(CreationError::Negative)) ); } #[test] fn test_zero() { assert_eq!( parse_positive("0"), Err(Error::Creation(CreationError::Zero)) ); } #[test] fn test_positive() { let x = PositiveInteger::new(42); assert!(x.is_ok()); assert_eq!(parse_positive("42"), Ok(x.unwrap())); } } }
06-from-into
#![allow(unused)] fn main() { //! Your task is to implement the `From` trait for `Person` to make creating //! Persons easier. //! //! The From trait is used for value-to-value conversions. When From is //! implemented for a source type, the Into trait is automatically implemented //! for the target type. You can read more about it at //! https://doc.rust-lang.org/std/convert/trait.From.html //! //! As a side note, ignore the fact that it makes absolutely no sense that a //! person has "default values". (This exercise is taken from Rustlings) #[derive(Debug, PartialEq)] struct Person { name: String, age: usize, } // We implement the `Default` trait to use it as a fallback when the provided // string is not convertible into a Person object. Note that this is not good // practice, one should use a `Result` rather. impl Default for Person { fn default() -> Person { Person { name: String::from("John"), age: 30, } } } // Steps: // 1. If the length of the provided string is 0, then return the default of // Person // 2. Split the given string on the commas present in it // 3. Extract the first element from the split operation and use it as the name // 4. If the name is empty, then return the default of Person // 5. Extract the other element from the split operation and parse it into a // `usize` as the age If while parsing the age, something goes wrong, then // return the default of Person Otherwise, then return an instantiated Person // object with the results impl From<&str> for Person { fn from(string: &str) -> Self { let mut split = string.split(','); // Read first three elements; third one is used to check that there is no garbage let (name, age, extra) = (split.next(), split.next(), split.next()); // Filter out empty names let name = name.filter(|name| name.len() > 0); // Filter out non-numerical values in age let age = age.and_then(|age| usize::from_str_radix(age, 10).ok()); match (name, age, extra) { (Some(name), Some(age), None) => Person { name: name.to_string(), age, }, _ => Person::default(), } } } // Do not edit anything below #[test] fn _main() { // Use the `from` associated function let p1 = Person::from("Mark,20"); // Use the `into` method let p2: Person = "Gerald,70".into(); println!("{:?}", p1); println!("{:?}", p2); } #[cfg(test)] mod tests { use super::*; #[test] fn default() { // Test that the default person is 30 year old John let dp = Person::default(); assert_eq!(dp.name, "John"); assert_eq!(dp.age, 30); } #[test] fn empty_convert() { // Test that John is returned when an empty string is provided let p = Person::from(""); assert_eq!(p, Person::default()); } #[test] fn good_convert() { // Test that "Mark,20" works let p = Person::from("Mark,20"); assert_eq!(p.name, "Mark"); assert_eq!(p.age, 20); } #[test] fn bad_age() { // Test that "Mark,twenty" will return the default person due to an // error in parsing age let p = Person::from("Mark,twenty"); assert_eq!(p, Person::default()); } #[test] fn missing_comma_and_age() { let p = Person::from("Mark"); assert_eq!(p, Person::default()); } #[test] fn missing_age() { let p = Person::from("Mark,"); assert_eq!(p, Person::default()); } #[test] fn missing_name() { let p = Person::from(",1"); assert_eq!(p, Person::default()); } #[test] fn missing_name_and_age() { let p = Person::from(","); assert_eq!(p, Person::default()); } #[test] fn missing_name_and_invalid_age() { let p = Person::from(",one"); assert_eq!(p, Person::default()); } #[test] fn trailing_comma() { let p = Person::from("Mike,32,"); assert_eq!(p, Person::default()); } #[test] fn trailing_comma_and_some_string() { let p = Person::from("Mike,32,man"); assert_eq!(p, Person::default()); } } }
07-errors4
#![allow(unused)] fn main() { //! Your task is to implement the missing conversions to `Error`. //! //! If the solution seems repetitive, the `thiserror` exercise will scratch that //! itch. use std::num::ParseIntError; use std::str::FromStr; #[derive(PartialEq, Debug)] enum Error { /// A [`CreationError`] from creating the [`PositiveInteger`]. Creation(CreationError), /// A [`ParseIntError`] from parsing the input string. ParseInt(ParseIntError), } // TODO impl From... impl From<CreationError> for Error { fn from(e: CreationError) -> Self { Error::Creation(e) } } impl From<ParseIntError> for Error { fn from(e: ParseIntError) -> Self { Error::ParseInt(e) } } // Don't change anything below this line. // Note: using `FromStr` rather than `From<&str>` is better if the parsing can // fail. `from_str` is rarely used directly, but it's counterpart `str::parse` // uses it in the background. Read more about `FromStr`: // https://doc.rust-lang.org/std/str/trait.FromStr.html impl FromStr for PositiveNonzeroInteger { type Err = Error; fn from_str(s: &str) -> Result<PositiveNonzeroInteger, Self::Err> { let x: i64 = s.parse()?; Ok(PositiveNonzeroInteger::new(x)?) } } #[derive(PartialEq, Debug)] struct PositiveNonzeroInteger(u64); #[derive(PartialEq, Debug)] enum CreationError { Negative, Zero, } impl PositiveNonzeroInteger { fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> { match value { 0 => Err(CreationError::Zero), 1.. => Ok(PositiveNonzeroInteger(value as u64)), _ => Err(CreationError::Negative), } } } #[cfg(test)] mod test { use super::*; #[test] fn test_parse_error() { assert!(matches!( PositiveNonzeroInteger::from_str("not a number"), Err(Error::ParseInt(_)) )); } #[test] fn test_negative() { assert_eq!( PositiveNonzeroInteger::from_str("-555"), Err(Error::Creation(CreationError::Negative)) ); } #[test] fn test_zero() { assert_eq!( PositiveNonzeroInteger::from_str("0"), Err(Error::Creation(CreationError::Zero)) ); } #[test] fn test_positive() { let x = PositiveNonzeroInteger::new(42); assert!(x.is_ok()); assert_eq!(PositiveNonzeroInteger::from_str("42").unwrap(), x.unwrap()); } } }
08-thiserror
Cargo.toml
[package]
name = "week5_thiserror"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
thiserror = "1.0.37"
`main.rs
//! Your task is to implement the conversions to `Error` by deriving
//! `thiserror::Error`. You don't need to add any impls.
//!
//! - Step one: add `thiserror` as a dependency
//! - Step two: read how to use `thiserror`
//! https://docs.rs/thiserror/latest/thiserror/ and fix the code.
use std::num::ParseIntError;
use std::str::FromStr;
use thiserror::Error;
#[derive(PartialEq, Debug, Error)]
enum Error {
/// A [`CreationError`] from creating the [`PositiveInteger`].
#[error("creating PositiveInteger failed: {0}")]
Creation(#[from] CreationError),
/// A [`ParseIntError`] from parsing the input string.
#[error("parsing failed: {0}")]
ParseInt(#[from] ParseIntError),
}
#[derive(PartialEq, Debug, Error)]
enum CreationError {
#[error("number is negative")]
Negative,
#[error("number is zero")]
Zero,
}
// Don't change anything below this line.
impl FromStr for PositiveNonzeroInteger {
type Err = Error;
fn from_str(s: &str) -> Result<PositiveNonzeroInteger, Self::Err> {
let x: i64 = s.parse()?;
Ok(PositiveNonzeroInteger::new(x)?)
}
}
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
match value {
0 => Err(CreationError::Zero),
1.. => Ok(PositiveNonzeroInteger(value as u64)),
_ => Err(CreationError::Negative),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_error() {
let res = PositiveNonzeroInteger::from_str("not a number");
assert!(matches!(res, Err(Error::ParseInt(_))));
assert_eq!(
format!("{}", res.unwrap_err()),
"parsing failed: invalid digit found in string"
);
}
#[test]
fn test_negative() {
let res = PositiveNonzeroInteger::from_str("-555");
assert_eq!(res, Err(Error::Creation(CreationError::Negative)));
assert_eq!(
format!("{}", res.unwrap_err()),
"creating PositiveInteger failed: number is negative"
);
}
#[test]
fn test_zero() {
let res = PositiveNonzeroInteger::from_str("0");
assert_eq!(res, Err(Error::Creation(CreationError::Zero)));
assert_eq!(
format!("{}", res.unwrap_err()),
"creating PositiveInteger failed: number is zero"
);
}
#[test]
fn test_positive() {
let x = PositiveNonzeroInteger::new(42);
assert!(x.is_ok());
assert_eq!(PositiveNonzeroInteger::from_str("42").unwrap(), x.unwrap());
}
}
09-semver
#![allow(unused)] fn main() { //! Your task is to define the struct Semver and implement PartialEq and //! PartialOrd for it. You also need to implement FromStr for Semver for //! convenience. use std::cmp::Ordering; use std::num::ParseIntError; use std::str::FromStr; // You are not allowed to derive more traits #[derive(Debug)] struct Semver { major: u32, minor: u32, patch: u32, } impl PartialEq for Semver { fn eq(&self, other: &Self) -> bool { self.major == other.major && self.minor == other.minor && self.patch == other.patch } } impl PartialOrd for Semver { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { let ord = self .major .cmp(&other.major) .then(self.minor.cmp(&other.minor)) .then(self.patch.cmp(&other.patch)); Some(ord) } } impl Semver { pub fn new(major: u32, minor: u32, patch: u32) -> Self { Self { major, minor, patch, } } pub fn is_stable(&self) -> bool { self.major > 0 } pub fn breaking(&self) -> Self { if self.is_stable() { Self::new(self.major + 1, 0, 0) } else { Self::new(0, self.minor + 1, 0) } } pub fn bump(&self) -> Self { if self.is_stable() { Self::new(self.major, self.minor + 1, 0) } else { Self::new(0, self.minor, self.patch + 1) } } } impl FromStr for Semver { type Err = ParseIntError; fn from_str(s: &str) -> Result<Self, Self::Err> { // Collecting Results into a Result catches the first Err let mut versions: Result<Vec<_>, _> = s.split('.').map(|v| u32::from_str_radix(v, 10)).collect(); let versions = versions?; match &versions[..] { &[major, minor, patch] => Ok(Self::new(major, minor, patch)), // As there is no custom error for this, we will panic _ => panic!("wrong number of version numbers"), } } } #[cfg(test)] mod tests { use super::*; #[test] fn from_str() { let error = "a.0.0".parse::<Semver>().unwrap_err(); assert_eq!(error.kind(), &std::num::IntErrorKind::InvalidDigit); let error = "0.1.c".parse::<Semver>().unwrap_err(); assert_eq!(error.kind(), &std::num::IntErrorKind::InvalidDigit); let error = "0.1.4294967296".parse::<Semver>().unwrap_err(); assert_eq!(error.kind(), &std::num::IntErrorKind::PosOverflow); } #[test] fn is_stable() { let version1: Semver = "1.61.0".parse().unwrap(); let version2: Semver = "0.10.5".parse().unwrap(); assert!(version1.is_stable()); assert!(!version2.is_stable()); } #[test] fn major_comparison() { let version1: Semver = "1.61.0".parse().unwrap(); let version2: Semver = "2.0.5".parse().unwrap(); assert!(version1 < version2); } #[test] fn minor_comparison() { let version1: Semver = "1.0.5".parse().unwrap(); let version2: Semver = "1.61.0".parse().unwrap(); assert!(version1 < version2); } #[test] fn patch_comparison() { let version1: Semver = "1.9.5".parse().unwrap(); let version2: Semver = "1.9.15".parse().unwrap(); assert!(version1 < version2); } #[test] fn breaking_change() { let version: Semver = "1.61.0".parse().unwrap(); let next = version.breaking(); assert_eq!(next, "2.0.0".parse().unwrap()); let version: Semver = "0.2.0".parse().unwrap(); let next = version.breaking(); assert_eq!(next, "0.3.0".parse().unwrap()); } #[test] fn minor_bump() { let version: Semver = "1.61.0".parse().unwrap(); let next = version.bump(); assert_eq!(next, "1.62.0".parse().unwrap()); let version: Semver = "0.2.0".parse().unwrap(); let next = version.bump(); assert_eq!(next, "0.2.1".parse().unwrap()); } } }
10-traits
#![allow(unused)] fn main() { //! Your task is to implement vector addition and scaling using built-in //! operators `+` and `*`. //! //! See https://doc.rust-lang.org/std/ops/trait.Add.html and //! https://doc.rust-lang.org/std/ops/trait.Mul.html for reference. use std::ops::{Add, Mul}; #[derive(Debug, PartialEq)] struct Vec3 { x: f32, y: f32, z: f32, } impl Vec3 { fn new(x: f32, y: f32, z: f32) -> Self { Self { x, y, z } } } impl Add<Vec3> for Vec3 { type Output = Vec3; fn add(self, rhs: Vec3) -> Self::Output { Self::Output { x: self.x + rhs.x, y: self.y + rhs.y, z: self.z + rhs.z, } } } impl Mul<f32> for Vec3 { type Output = Vec3; fn mul(self, rhs: f32) -> Self::Output { Self::Output { x: self.x * rhs, y: self.y * rhs, z: self.z * rhs, } } } impl Mul<Vec3> for f32 { type Output = Vec3; fn mul(self, rhs: Vec3) -> Self::Output { Self::Output { x: self * rhs.x, y: self * rhs.y, z: self * rhs.z, } } } #[test] fn add() { assert_eq!( Vec3::new(1.0, 2.0, -1.0) + Vec3::new(3.0, -2.0, 2.0), Vec3::new(4.0, 0.0, 1.0) ); } #[test] fn mul() { assert_eq!(2.0 * Vec3::new(3.0, -2.0, 2.0), Vec3::new(6.0, -4.0, 4.0)); assert_eq!(Vec3::new(3.0, -2.0, 2.0) * 2.0, Vec3::new(6.0, -4.0, 4.0)); } }
11-recursive_types
#![allow(unused)] fn main() { //! Your task is to implement the methods for `Crate`. use std::{iter::Sum, ops::Add}; #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] struct Kilograms(f32); impl From<f32> for Kilograms { fn from(kilos: f32) -> Self { Self(kilos) } } impl From<i32> for Kilograms { fn from(kilos: i32) -> Self { Self(kilos as f32) } } impl Add<Self> for Kilograms { type Output = Self; fn add(self, rhs: Self) -> Self::Output { Kilograms::from(self.0 + rhs.0) } } impl Sum for Kilograms { fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { iter.fold(0.0.into(), |acc, next| acc + next) } } #[derive(Debug, Clone, PartialEq, PartialOrd)] enum Crate { Empty, WithStuff(Kilograms), Multiple(Vec<Self>), } impl Default for Crate { fn default() -> Self { Self::Empty } } #[derive(Debug, Clone, PartialEq)] enum Error { /// Tried to fill a crate with stuff but there were other crates inside FillMultiple, /// Tried to unbox an empty crate EmptyCrate, } impl Crate { fn new() -> Self { Self::default() } /// Returns the total mass of the crate's contents. fn mass(&self) -> Kilograms { match self { Self::Empty => Kilograms(0.0), Self::WithStuff(mass) => *mass, Self::Multiple(crates) => crates.iter().map(|c| c.mass()).sum(), } } /// Fills the crate with `mass` kilograms of _stuff_. /// /// # Errors /// /// If the crate contains multiple crates there's no way to fill it and an /// [`Error::FillMultiple`] is returned. fn fill(&mut self, mass: Kilograms) -> Result<(), Error> { match self { Self::Empty => { *self = Self::WithStuff(mass); Ok(()) } Self::WithStuff(old) => { *self = Self::WithStuff(*old + mass); Ok(()) } Self::Multiple(_) => Err(Error::FillMultiple), } } /// Unboxes the crate returing the mass of all the _stuff_. Every unboxed /// crate becomes empty and stays in their parent crate. If the crate /// contains empty crates and no _stuff_ the unboxing is successful. /// /// # Errors /// /// If the top-most crate is empty an [`Error::EmptyCrate`] is returned. fn unbox(&mut self) -> Result<Kilograms, Error> { match self { Self::Empty => Err(Error::EmptyCrate), Self::WithStuff(mass) => { let mass = *mass; // Create a copy *self = Self::Empty; Ok(mass) } Self::Multiple(crates) => { let total_mass = crates .into_iter() .filter_map(|c| c.unbox().ok()) .sum::<Kilograms>(); Ok(total_mass) } } } /// Inserts an `other` crate inside `self`. If the crate has _stuff_ inside, /// its mass is returned. If the crate has other crates inside, the `other` /// crate just joins them. fn insert(&mut self, other: Self) -> Option<Kilograms> { match self { Self::Empty => { *self = Self::Multiple(vec![other]); None } Self::WithStuff(mass) => { let mass = *mass; // Create a copy *self = other; Some(mass) } Self::Multiple(crates) => { crates.push(other); None } } } } #[test] fn mass() { let heavy_crate = Crate::WithStuff(12.into()); let loota = Crate::Multiple(vec![ heavy_crate.clone(), Crate::Multiple(vec![heavy_crate.clone(), Crate::new()]), ]); assert_eq!(loota.mass(), 24.into()); } #[test] fn fill() { let mut loota = Crate::Empty; loota.fill(5.into()); assert_eq!(loota.mass(), 5.into()); let mut loota = Crate::WithStuff(2.into()); loota.fill(5.into()); assert_eq!(loota.mass(), 7.into()); } #[test] fn unbox() { let mut loota = Crate::Empty; assert_eq!(loota.unbox(), Err(Error::EmptyCrate)); assert_eq!(loota, Crate::Empty); let mut loota = Crate::Multiple(vec![Crate::Empty, Crate::Multiple(vec![Crate::Empty])]); assert_eq!(loota.unbox(), Ok(0.into())); let iso_loota = Crate::WithStuff(20.into()); let mut loota = Crate::Multiple(vec![ iso_loota.clone(), Crate::Multiple(vec![iso_loota.clone(), Crate::new()]), iso_loota.clone(), Crate::new(), ]); assert_eq!(loota.unbox(), Ok(60.into())); assert_eq!( loota, Crate::Multiple(vec![ Crate::Empty, Crate::Multiple(vec![Crate::Empty, Crate::Empty]), Crate::Empty, Crate::Empty ]) ); } #[test] fn insert() { let pieni_loota = Crate::WithStuff(2.into()); let iso_loota = Crate::Multiple(vec![ pieni_loota.clone(), Crate::Multiple(vec![pieni_loota.clone(), Crate::new()]), pieni_loota.clone(), ]); let mut loota = Crate::Empty; assert_eq!(loota.insert(iso_loota.clone()), None); assert_eq!(loota, Crate::Multiple(vec![iso_loota.clone()])); let mut loota = Crate::Multiple(vec![ pieni_loota.clone(), Crate::Multiple(vec![Crate::Empty]), ]); assert_eq!(loota.insert(iso_loota.clone()), None); assert_eq!(loota.mass(), 8.into()); let mut loota = Crate::WithStuff(10.into()); assert_eq!(loota.insert(iso_loota), Some(10.into())); assert_eq!(loota.mass(), 6.into()); } }
12-lifetimes1
#![allow(unused)] fn main() { //! Your task is to fix the lifetime errors. Read the chapter about lifetimes: //! https://doc.rust-lang.org/stable/book/ch10-03-lifetime-syntax.html fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } fn always_first<'a>(x: &'a str, y: &'_ str) -> &'a str { x } // Do not edit anything below #[test] fn _main() { let s1 = "static string literal"; { let s2 = "not static".to_string(); let longest = longest(s1, &s2); assert_eq!(longest, "static string literal"); }; // longest could not live here as it cannot outlive s2 regardless of which // string is longer let first = { let s2 = "not static".to_string(); always_first(s1, &s2) }; // first should be able to live here as it ignores the lifetime of the // second argument assert_eq!(first, "static string literal"); } }
13-as_ref_mut
#![allow(unused)] fn main() { //! Your task is to change the functions `byte_counter` and //! `capitalize_first_word` to support different types of (be generic over) //! references. More specifically you need to support a `&String` in place of a //! `&str` and a `&mut String` in place of a `&mut str`. //! //! The traits AsRef and AsMut allow for cheap reference-to-reference (compared //! to From/Into value-to-value) conversions. Read more about them at //! - https://doc.rust-lang.org/std/convert/trait.AsRef.html //! - https://doc.rust-lang.org/std/convert/trait.AsMut.html /// Obtain the number of bytes (not characters) in the given argument fn byte_counter(s: impl AsRef<str>) -> usize { let s = s.as_ref(); s.as_bytes().len() } /// Capitalize first word in a sentence fn capitalize_first_word(mut s: impl AsMut<str>) { let s = s.as_mut(); let i = s.find(' ').unwrap_or(s.len()); s[..i].make_ascii_uppercase(); } // Don't change the tests! #[cfg(test)] mod tests { use super::*; #[test] fn bytes() { // é takes two bytes let s = "Café au lait"; assert_eq!(byte_counter(s), 13); let s = s.to_string(); assert_eq!(byte_counter(s), 13); } #[test] fn capitalize_word() { let mut s = "word".to_string(); capitalize_first_word(&mut s); assert_eq!(s, "WORD"); } #[test] fn capitalize_ascii() { let mut s = "Cafe au lait".to_string(); let sref: &mut str = s.as_mut_str(); capitalize_first_word(sref); assert_eq!(s, "CAFE au lait"); } #[test] fn capitalize_ascii_only() { let mut s = "Caffè latte".to_string(); capitalize_first_word(&mut s); assert_eq!(s, "CAFFè latte"); } } }
14-bst
Saat palautteen tehtävästä palauttamalla sen opettajalle.