Esimerkkiratkaisut

01-enums

#![allow(unused)]
fn main() {
//! Your task is to define the variants of the enumeration. Do not edit `main`.

#[derive(Debug)] // This makes printing the instances with {:?} (debug format) possible
enum Message {
    Quit,
    Echo,
    Move,
    ChangeColor,
}

#[test]
fn _main() {
    println!("{:?}", Message::Quit);
    println!("{:?}", Message::Echo);
    println!("{:?}", Message::Move);
    println!("{:?}", Message::ChangeColor);
}
}

02-structs

#![allow(unused)]
fn main() {
//! Your task is to create the missing structs and fill in the missing parts of
//! the tests. Read the chapter about structs (whether you need help or not)
//! https://doc.rust-lang.org/book/

#[derive(Debug)]
struct ColorClassicStruct {
    name: String,
    hex: String,
}

#[derive(Debug)]
struct ColorTupleStruct<'a>(&'a str, &'a str);

#[derive(Debug)]
struct UnitStruct;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn classic_structs() {
        let green = ColorClassicStruct {
            name: "green".to_string(),
            hex: "#00FF00".to_string(),
        };

        assert_eq!(green.name, "green");
        assert_eq!(green.hex, "#00FF00");
    }

    #[test]
    fn tuple_structs() {
        let green = ColorTupleStruct("green", "#00FF00");

        assert_eq!(green.0, "green");
        assert_eq!(green.1, "#00FF00");
    }

    #[test]
    fn unit_structs() {
        let unit_struct = UnitStruct;
        let message = format!("{:?}s are fun!", unit_struct);

        assert_eq!(message, "UnitStructs are fun!");
    }
}
}

03-enums2

#![allow(unused)]
fn main() {
//! Your task is to fix the `is_quit` function. Make good use of your favorite
//! feature of Rust: `match`. Bonus points if you can do it with `matches!`.

#[derive(Debug)]
enum Message {
    Quit,
    Echo,
    Move,
    ChangeColor,
}

fn is_quit(msg: Message) -> bool {
    match msg {
        Message::Quit => true,
        _ => false,
    }
}

#[test]
fn _main() {
    assert!(is_quit(Message::Quit));
    assert!(!is_quit(Message::Echo));
}
}

04-newtype

#![allow(unused)]
fn main() {
//! Your task is to implement `to_miles` for `Kilometers` and fill in the blank
//! in `main`.

// These structs are called newtype structs. Read more about the newtype design
// pattern:
// https://rust-unofficial.github.io/patterns/patterns/behavioural/newtype.html
struct Miles(f64);
struct Kilometers(f64);

impl Miles {
    fn to_kilometers(self) -> Kilometers {
        Kilometers(1.6 * self.0)
    }
}

impl Kilometers {
    fn to_miles(self) -> Miles {
        Miles(self.0 / 1.6)
    }
}

fn travel(distance: Miles) -> String {
    format!("Travelled {} miles", distance.0)
}

fn travel_kilometers(distance: Kilometers) -> String {
    travel(distance.to_miles())
}

#[test]
fn _main() {
    let msg = travel_kilometers(Kilometers(10.0));

    assert_eq!(&msg, "Travelled 6.25 miles");
}
}

05-derive

#![allow(unused)]
fn main() {
//! Your task is to derive required traits for `Foo`. You might find *the book*
//! helpful: https://doc.rust-lang.org/book/appendix-03-derivable-traits.html

#[derive(Default, Debug, Clone, PartialEq)]
struct Foo {
    numbers: Vec<i32>,
}

#[test]
fn _main() {
    let foo = Foo::default();
    dbg!(foo.clone());

    assert_eq!(foo, Foo { numbers: vec![] })
}
}

06-type-safety

#![allow(unused)]
fn main() {
//! Your task is convert `delete_filesystem` to use DryRun instead of a boolean.
//! Read more about type safety guidelines here
//! https://rust-lang.github.io/api-guidelines/type-safety.html

#[derive(Debug)]
enum DryRun {
    /// Does not commit any changes
    Yes,
    /// Danger: everything will be lost
    No,
}

fn delete_filesystem(dry_run: DryRun) -> bool {
    match dry_run
    {
        DryRun::No => {
            println!("Deleting all files...");
            true
        },
        DryRun::Yes => {
            println!("Not deleting files...");
            false
        }
    }
}

#[test]
fn _main() {
    let deleted = delete_filesystem(DryRun::No);

    assert_eq!(deleted, true);
}
}

07-match

#![allow(unused)]
fn main() {
//! Your task is to implement the match
//!
//! Part 1: Write the match statement in `send_message` to "do the correct
//! stuff".
//!
//! Part 2: You may need to add more messages to the array `messages`.

/// A position in 2D space
#[derive(Debug, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

/// State of the abstract high-level frobnicator
#[derive(Debug)]
struct State {
    location: Point,
    running: bool,
}

/// Messages sent to the [`State`] frobnicator
#[derive(Debug)]
enum Message {
    /// Stops the frobnicator
    Quit,
    /// Moves the frobnicator
    Move { x: i32, y: i32 },
}

impl State {
    fn new() -> Self {
        Self {
            location: Point { x: 0, y: 0 },
            running: true,
        }
    }

    fn send_message(&mut self, msg: Message) {
        match msg {
            Message::Quit => self.running = false,
            Message::Move { x, y } => {
                let x = self.location.x + x;
                let y = self.location.y + y;
                self.location = Point { x, y }
            },
        }
    }
}

#[test]
fn _main() {
    let messages = [
        Message::Move { x: 1, y: 3 },
        Message::Move { x: -2, y: -1 },
        Message::Quit,
    ];

    let mut state = State::new();

    for msg in messages {
        state.send_message(msg);
    }

    assert_eq!(state.location, Point { x: -1, y: 2 });
    assert!(!state.running);
}
}

08-if-let

#![allow(unused)]
fn main() {
//! Your task is to fill in the blanks. Don't change anything outside the `if
//! let`. Check https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html
//! around "Destructuring Structs" for the answer.

enum Message {
    Quit,
    Move { x: i32, y: i32 },
}

#[test]
fn _main() {
    let msg = Message::Move { x: 1, y: 2 };

    let mut x = 2;
    let mut y = 3;

    if let Message::Move { x: a, y: b } = msg {
        x += a;
        y += b;
    }

    assert_eq!(x, 3);
    assert_eq!(y, 5);
}
}

09-match2

#![allow(unused)]
fn main() {
//! Your task is to fill in the match in `execute`.

#[derive(Debug, PartialEq)]
enum Cli {
    Run(Option<String>),
    Rename(String, Option<String>),
}

impl Cli {
    pub fn execute(&self) -> Option<String> {
        use Cli::*; // Makes all variants visible without Cli prefix
        match self {
            Run(None) | Rename(_, None) => None,
            Run(Some(file)) => Some(format!("running {file}").to_string()),
            Rename(a, Some(b)) => Some(format!("renaming {a} to {b}").to_string()),
        }
    }
}

#[test]
fn _main() {
    assert_eq!(
        Cli::Run(Some("ferris.rs".to_string())).execute().unwrap(),
        "running ferris.rs"
    );
    assert_eq!(
        Cli::Rename("ferris.rs".to_string(), Some("corro.c".to_string()))
            .execute()
            .unwrap(),
        "renaming ferris.rs to corro.c"
    );
    assert_eq!(Cli::Rename("/etc/shadow".to_string(), None).execute(), None);
    assert_eq!(Cli::Run(None).execute(), None);
}
}

10-option

#![allow(unused)]
fn main() {
//! Your task is to fix the type errors and initialize the `numbers` array.
//!
//! Read more about Options in the book chapter 6.1
//! https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html

// Do not change this function
fn print_number(maybe_number: Option<u16>) {
    println!("maybe {:?}", maybe_number);
}

#[test]
fn _main() {
    print_number(Some(13));
    print_number(Some(99));

    let mut numbers = [None; 5];

    for iter in 0..5 {
        let number_to_add: u16 = ((iter * 1235) + 2) / (4 * 16);

        numbers[iter as usize] = Some(number_to_add);
    }

    assert_eq!(numbers, [Some(0), Some(19), Some(38), Some(57), Some(77)]);
}
}

11-option-iter

#![allow(unused)]
fn main() {
//! Your task is to fill in all the blanks. Nothing else should need fixing.
//! Read more about iterators and their adaptors in
//! https://doc.rust-lang.org/book/ch13-02-iterators.html If too hard, try
//! harder.

/// Divides `dividend` by `divisor` safely, i.e. never divides by zero. A divide
/// by zero is represented with `None`
fn safe_div(dividend: i32, divisor: i32) -> Option<f32> {
    if divisor == 0 {
        None
    } else {
        Some(dividend as f32 / divisor as f32)
    }
}

/// Returns a list of `Some`s with all integers from -5 to 4 inside
fn list_of_options() -> Vec<i32> {
    (-5..5).into_iter().collect()
}

#[test]
fn _main() {
    let list = list_of_options();

    let pair_fractions: Vec<Option<f32>> = list
        .windows(2)
        .map(|iter| if iter[1] == 0 { None } else { Some(iter[0] as f32 / iter[1] as f32)} )
        .collect();

    use std::convert::identity; // Super helpful morphism, right?

    let sum: f32 = pair_fractions.into_iter().filter_map(identity).sum();

    assert_eq!(sum, 8.0);
}
}

B1-lifetimes

#![allow(unused)]
fn main() {
//! Your task is to fill in the generic parameters to `Person` and fix the
//! dangling reference issue however you wish.
//!
//! Note that a `String` can never be static as it includes an allocation (the
//! crate lazy_static solves that issue, but is not needed here).

#[derive(Debug)]
struct Person<'a> {
    pub name: &'a str,
    pub age: i32,
}

fn make_person() -> Person<'static> {
    let name = "Michael Stevens";

    Person {
        name: &name,
        age: 36,
    }
}

#[test]
fn _main() {
    let michael = make_person();

    assert_eq!(michael.age, 36)
}
}

B2-config-parser

//! Your task is to add missing fields to `Config` which are present in
//! `config.toml` at `exercises/week4/BX-config-parser/config.toml`. You also
//! need to add new structs to accommodate the data.
//!
//! You should start by reading what serde is about: https://serde.rs/.

#![allow(dead_code)] // In this example we don't use the parsed data

use std::{collections::HashMap, fs::read_to_string, net::Ipv4Addr, path::Path};

use serde::Deserialize;
use toml::{from_str, value::Datetime, Value};

#[derive(Debug, Deserialize)]
struct Config {
    title: String,
    owner: Owner,
    database: Database,
    servers: HashMap<String, Server>,
}

#[derive(Debug, Deserialize)]
struct Owner {
    name: String,
    dob: Datetime,
}

#[derive(Debug, Deserialize)]
struct Database {
    enabled: bool,
    ports: Vec<u16>,
    /// A vector of arbitrary toml values, can be heterogeneous
    data: Vec<Value>,
    temp_targets: HashMap<String, f32>,
}

#[derive(Debug, Deserialize)]
struct Server {
    ip: Ipv4Addr,
    role: String,
}

impl Config {
    pub fn parse(path: impl AsRef<Path>) -> anyhow::Result<Self> {
        let path = path.as_ref();
        toml::from_str(&read_to_string(path)?).map_err(anyhow::Error::from)
    }
}

fn main() -> anyhow::Result<()> {
    let config: Config = from_str(&read_to_string("config.toml")?)?;

    println!("the parsed config is: {:#?}", config);

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn name_is_tom() {
        let config = Config::parse("config.toml").unwrap();

        assert!(config.owner.name.starts_with("Tom"));
    }

    #[test]
    fn open_ports() {
        let config = Config::parse("config.toml").unwrap();

        assert_eq!(config.database.ports.len(), 3);
    }

    #[test]
    fn reasonable_temp_targets() {
        let config = Config::parse("config.toml").unwrap();

        assert!(config
            .database
            .temp_targets
            .iter()
            .all(|(_key, &target)| target < 100.0));
    }

    #[test]
    fn server_ips_different() {
        let config = Config::parse("config.toml").unwrap();

        let alpha = config.servers.get("alpha").unwrap();
        let beta = config.servers.get("beta").unwrap();

        assert_ne!(alpha.ip, beta.ip);
    }
}

B3-type-safety-generics

#![allow(unused)]
fn main() {
//! Your task is to implement the three required methods for `Rocket<Launched>`
//! to make the tests pass. Consult the tests for guidance.
//!
//! Bonus bonus task: Add documentation comments to all Rocket's methods.

use std::marker::PhantomData;

const ESCAPE_VELOCITY: u32 = 11_186;

/// A rocket sitting on the ground with at least one cat
#[derive(Debug)]
struct Grounded;
/// A rocket flying in outer space with some velocity
#[derive(Debug)]
struct Launched;
/// A rocket crashed due to undefined behavior with no cat videos left
#[derive(Debug)]
struct Crashed;

#[derive(Debug)]
struct Rocket<Stage = Grounded> {
    stage: PhantomData<Stage>,
    velocity: u32,
    crew: u32,
}

impl Rocket<Grounded> {
    pub fn new() -> Self {
        Self {
            stage: PhantomData,
            velocity: 0, // Not moving
            crew: 1,     // One captain
        }
    }

    pub fn add_crew(&mut self, more_cats: u32) {
        self.crew += more_cats;
    }

    pub fn launch(self) -> Rocket<Launched> {
        assert_eq!(self.velocity, 0); // This is harder to make invariant using types
        Rocket::<Launched> {
            stage: PhantomData,
            velocity: ESCAPE_VELOCITY,
            crew: self.crew,
        }
    }
}

impl Rocket<Launched> {
    pub fn accelerate(&mut self, v: u32) {
        self.velocity += v;
    }

    pub fn decelerate(&mut self, v: u32) {
        self.velocity -= v;
    }

    pub fn try_land(self) -> Result<Rocket::<Grounded>, Rocket::<Crashed>> {
        if self.velocity == 0 {
            Ok(Rocket::<Grounded>
               {
                   stage: PhantomData,
                   velocity: self.velocity,
                   crew: self.crew,
               })
        } else {
            Err(Rocket::<Crashed> {
                   stage: PhantomData,
                   velocity: self.velocity,
                   crew: 0,
            })
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn grounded_rocket() {
        let mut rocket = Rocket::new();
        rocket.add_crew(5);

        assert!(matches!(
            rocket,
            Rocket::<Grounded> {
                crew: 6,
                velocity: 0,
                ..
            }
        ))
    }

    /// Rocket<Launched> should be able to change its velocity with `accelerate`
    #[test]
    fn launched_rocket() {
        let mut rocket = Rocket::new();
        rocket.add_crew(5);

        let mut rocket = rocket.launch();
        rocket.accelerate(100);
        assert!(matches!(
            rocket,
            Rocket::<Launched> {
                crew: 6,
                velocity: 11_286,
                ..
            }
        ))
    }

    /// Rocket<Launched> should be able to `try_land`, which will be successful
    /// if its velocity is zero, otherwise it will crash with no cat videos left
    #[test]
    fn houston_we_have_had_a_problem() {
        let mut rocket = Rocket::new();

        let rocket = rocket.launch();
        let crashed: Result<Rocket, Rocket<Crashed>> = rocket.try_land();
        assert_eq!(crashed.unwrap_err().crew, 0)
    }

    /// Rocket<Launched> should be able to change its velocity with `decelerate`
    #[test]
    fn landing_successful() {
        let mut rocket = Rocket::new();
        rocket.add_crew(5);

        let mut rocket = rocket.launch();
        rocket.decelerate(11_186);
        let landed = rocket.try_land();
        assert!(matches!(
            landed,
            Ok(Rocket::<Grounded> {
                crew: 6,
                velocity: 0,
                ..
            })
        ))
    }
}
}

B4-reqwest

fn _main() {
    let url = "https://fablab.rip";

    println!("Fetching {:?}...", url);

    let res = reqwest::blocking::get(url).unwrap();

    println!("Response: {:?}", res.text().unwrap());
}

B5-closures

#![allow(unused)]
fn main() {
//! Your task is to fix the returned closure from `is_garbage`.

fn is_garbage() -> impl Fn(&str) -> bool {
    let no_garbage = vec!["C", "C++", "Rust"];
    move |lang: &str| -> bool { !no_garbage.contains(&lang) }
}

#[test]
fn _main() {
    let is_garbage = is_garbage();
    assert!(is_garbage("Java"));
    assert!(is_garbage("Python"));
    assert!(is_garbage("TypeScript"));
}
}

B6-closure-traits

#![allow(unused)]
fn main() {
//! Your task is to implement `evaluate` for `Map`, and define the `square`
//! closure.

struct Map<F>
where
    F: Fn(i32) -> i32,
{
    data: Vec<i32>,
    mapper: F,
}

impl<F> Map<F>
where
    F: Fn(i32) -> i32,
{
    fn new(data: Vec<i32>, mapper: F) -> Self {
        Self { data, mapper }
    }

    /// Maps each element in `data` using `mapper` in-place
    fn evaluate(&mut self) {
        for num in self.data.iter_mut() {
            *num = (self.mapper)(*num);
        }
    }

    fn into_vec(self) -> Vec<i32> {
        self.data
    }
}

#[test]
fn _main() {
    let square = |x| x * x;

    let mut map = Map::new(vec![1, 2, 3, 4], square);

    map.evaluate();
    map.evaluate();

    assert_eq!(map.into_vec(), [1, 16, 81, 256]);
}
}