Help me get the most from this Rustling (try_from_into.rs)

SPOILERS BELOW for Rustlings!
Rustlings is a series of small exercises "to get you used to reading and writing Rust code."
In the Conversions exercises I just got try_from_into.rs to compile and pass its tests.

I have shared my solution below. And I am looking for comments and discussion on the exercise as a whole.

Am i getting the lesson being taught here? Below, as you'll see, I've commented out an implementation that compiles but fails the tests. I still don't understand why attempting to convert a number < 0 && > 255 to u8 does not result in the kind of error required to pass the test. Is there a way of succeeding here without using the if r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 line?

This Rustling has been updated over its lifetime. Many of the existing solutions online contain a different String error type. As someone relatively new to coding as a whole I was confused as to what was being required. It was frustrating at the end to realize all I needed to do to make my code compile was to write my own "error" and then .into() to return the right Error result. Again, I feel like I'm missing the broader significance of how errors are handled here.

Really looking for discussions and comments that can show me really what this exercise is trying to bring home. I feel like I'm missing the bigger picture!

SPOILERS BELOW

Here is everything I added to the challenge to make it successfully compile and pass its tests:

// Tuple implementation
impl TryFrom<(i16, i16, i16)> for Color {
    type Error = Box<dyn error::Error>;
    fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
	match tuple {
            (r, g, b) if r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 => {
                Err("rgb must be 0~255".into())
            },
            (red, green, blue) => {
                Ok(Color {
                    red: red as u8, 
                    green: green as u8, 
                    blue: blue as u8,
                })
            }
        }
    }
}
	 /* BUT WHY DOESN'T THIS WORK?!? 
	if let (r, g, b) = (u8::try_from(tuple.0).unwrap(), u8::try_from(tuple.1).unwrap(), u8::try_from(tuple.2).unwrap()) {
	    Ok (Color {
		red: r, //u8::try_from(tuple.0).unwrap(),
		green: g, //u8::try_from(tuple.1).unwrap(),
		blue: b, //u8::try_from(tuple.2).unwrap(),
            })} else { Err("no good!".into()) }
    }
}
	*/
// Array implementation
impl TryFrom<[i16; 3]> for Color {
    type Error = Box<dyn error::Error>;
    fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {
	arr[..].try_into()
    }
}

// Slice implementation
impl TryFrom<&[i16]> for Color {
    type Error = Box<dyn error::Error>;
    fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {
	if slice.len() != 3 {
            return Err("There aren't enough values for RGB here!".into());
        }
	(slice[0], slice[1], slice[2]).try_into()
    }
}

Remember that you could use Range Patterns in match expressions:

impl TryFrom<(i16, i16, i16)> for Color {
    type Error = Box<dyn error::Error>;
    fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
        match tuple {
            (r @ 0..=255, g @ 0..=255, b @ 0..=255) => Ok(Color {
                red: r as u8,
                green: g as u8,
                blue: b as u8,
            }),
            _ => Err("rgb must be 0~255".into()),
        }
    }
}
3 Likes

The reason is that you've .unwrap() the result, which panic and crash the test case instead of returning Err. Check below if you want REAL SPOILER.

// Tuple implementation
impl TryFrom<(i16, i16, i16)> for Color {
    type Error = Box<dyn error::Error>;
    fn try_from((red, green, blue): (i16, i16, i16)) -> Result<Self, Self::Error> {
        Ok(Color {
            red: red.try_into()?,
            green: green.try_into()?,
            blue: blue.try_into()?,
        })
    }
}
2 Likes

Now I'll be able to remember this! I hadn't actually got all the way to the part of the book where we learn about @ Bindings Thanks so much!

1 Like

Whoa!
And this works, too:

red: tuple.0.try_into()?,
green: tuple.1.try_into()?,
blue: tuple.2.try_into()?,

I'm going to go back to the book and other guides and try to figure out how I was supposed to get this! Thank you so much, this is exactly what I couldn't figure out. And I feel that solving it this way is the point of the exercise. Really glad I asked! Thanks again!

As this is "try_from_into" here is the try_from implementation.

impl TryFrom<(i16, i16, i16)> for Color {
    type Error = Box<dyn error::Error>;
    fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
        let (red, green, blue) = tuple;
        Ok(Color{
            red: u8::try_from(red)?,
            green: u8::try_from(green)?,
            blue: u8::try_from(blue)?,
        })
    }
}
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.