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.