rustlings icon indicating copy to clipboard operation
rustlings copied to clipboard

options2: unclear what to do and why

Open Fxlr8 opened this issue 1 year ago • 5 comments

Hello Rustlings Team,

First of all thank you for this project. It was really fun solving it so far. I got really stuck and scratching my head at this one and I decided to ask you for an improvement.

    fn simple_option() {
        let target = "rustlings";
        let optional_target = Some(target);

        // TODO: Make this an if let statement whose value is "Some" type
        word = optional_target {
            assert_eq!(word, target);
        }
    }

Looks like this excercise is trying to show how to use Option<T> type in Rust. Currently, the exercise seems somewhat forced and does not effectively demonstrate the real-world use cases of the Option<T> type.

We start with a "rustlings" string literal and wrap it in optional_target. Then the comment asks us to Make this an if let statement whose value is "Some" type (which is a riddle by itself) providing us with some convoluted assertion. I am just learning Rust and have never seen this type before. If that task had some real-world scenario showing us why Option type is useful (for example safe parsing of data) that would really help.

Fxlr8 avatar Jun 09 '24 16:06 Fxlr8

I agree this part of the exercise is weird. Do you have an idea how to improve it? We should still teach about the if-let statement though. You are welcome to open a PR :D

mo8it avatar Jul 04 '24 11:07 mo8it

One problem is that it kind of looks like valid code. Coming from C++, Rust has some "weird" expressions (e.g. let ... else ... ;"). So when I first saw that code, my first thought was "what kind of Rust specific expression is that?", instead of "something is missing".

More specifically, it looks like a "normal" assignment where the rhs is optional_target{...} and so my thoughts weres like "are we creating a sort of a struct value of type optional_target?" and "are we creating a closure-like thing?".

Maybe be something like:

/* `if let` expression = optional_target*/ {
   assert_eq!(word, target);
}

Nahor avatar Jul 05 '24 19:07 Nahor

This exercise stumped if a bit too, even though I have used if-let and while-let constructs before. I think the users would benifit for more examples as to why we are doing this.

fn main() {
    println!("Hello, world!");
}

#[cfg(test)]
mod tests {

    #[test]
    fn naive() {
        // you'll see options everywhere in Rust code
        // they are a common abstraction and are infact
        // very useful
        let target = "rustlings";
        let optional_target = Some(target);

        // many a times you need to extract the value
        // out of an option IF it is Some() or IF it
        // satisfies a condition
        // we often want to extract the wrapped value
        // in the Some() and set it to another variable
        let word: &str;
        match optional_target {
            Some(w) => {
                word = w;
            },
            None => {
                word = "oh no something went wrong!"
            }
        };
        assert_eq!(word, target);
    }

    #[test]
    fn simple_option() {
        let target = "rustlings";
        let optional_target = Some(target);

        // we have the if let construct just for that use case
        // where we lift or extract a value out of an option
        if let Some(word) = optional_target {
            assert_eq!(word, target)
        }

        // see how concise and readable our code had now 
        // become?
    }

    #[test]
    fn naive_multiple() {
        let mut collection: Vec<Option<i32>> = vec![
            Some(1), Some(2), Some(3), Some(4), Some(0), Some(-32), 
            Some(45), Some(-213), Some(21321), Some(45787) 
        ];

        let mut answers: Vec<i32> = Vec::new();

        // now of course we can do the naive thing and repeat the if let
        // but Rust also gives us while let
        //
        // if you note number here has type Option<i32> shouldn't it be i32?
        // well no, because if you recall .pop() return and Option, so 
        // collection.pop() is returning Some(Some(i32)) and while let then 
        // extracts the inner value to get, Some(i32), 
        // while let Some(number) = collection.clone().pop() {
        // }

        // we need to extract it once more, by doing the following
        while let Some(Some(number)) = collection.pop() {
            answers.push(number);
        }

        assert_eq!(answers.iter().sum::<i32>(), 66918);

    }

    #[test]
    fn multiple_extractions() {
    }
}

I made the above code example, note that there are no exercises for the user to do, but if we provided some simple examples of if let and while let demonstrating why we need the constructs, it may help make the goal of the exercise more clear, Open to edits, changes and suggestions ofcourse.

I think this would also help with #655

frroossst avatar Sep 02 '24 18:09 frroossst

Currently stuck with Options2 as well. Up until this exercise everything was pretty smooth and good to understand, but I really struggle with what I am to do here.

fre-ben avatar Apr 29 '25 15:04 fre-ben

Managed to solve it now, but this line // TODO: Make this an if let statement whose value is "Some" type was difficult to understand, I've done if let patterns a few times before but I didn't understand what the problem wanted me to do.

My two cents:

  1. Can we take an Option from a common std function instead of creating our own so that is closer to a real scenario
  2. Maybe rephrase the TODO and the template provided (word = optional_target { ... }) so that it doesn't look like a closure/struct like @Nahor was mentioning, this was my first impression also!

Example:

    let text = "learning rust with rustlings";
    let position = text.find("rustlings");

    // TODO: Replace the `/* pattern */` and `/* value */` with the correct if-let syntax.
    if let /* pattern */ = /* value */ {
        assert_eq!(index, 19);
    }

lRaulMN7 avatar Jul 23 '25 16:07 lRaulMN7