rust 组合元组的TryFrom特征

hwazgwia  于 2023-10-20  发布在  其他
关注(0)|答案(2)|浏览(114)

下面是rustlings try_from练习的一部分的工作解决方案。

impl TryFrom<(i16, i16, i16)> for Color {
    type Error = IntoColorError;
    fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
        let (red, green, blue) = tuple;
        match (u8::try_from(red), u8::try_from(green), u8::try_from(blue)) {
            (Ok(red), Ok(green), Ok(blue)) => Ok(Color { red, green, blue }),
            _ => Err(Self::Error::IntConversion),
        }
    }
}

这是一个很好的解决方案,但有很多代码重复。有一个解决方案似乎是明智的,其中元组转换可以在单个表达式中完成。例如:

impl TryFrom<(i16, i16, i16)> for Color {
    type Error = IntoColorError;
    fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
        match <(u8, u8, u8)>::try_from(tuple) {
            Ok((red, green, blue)) => Ok(Color { red, green, blue }),
            _ => Err(Self::Error::IntConversion),
        }
    }
}

这不起作用,但编译器可以自动推断组合元组的TryFrom特征似乎是合理的。有没有什么方法可以让这种行为?

gblwokeq

gblwokeq1#

我认为在目前的稳定,你有什么是完全好的。在Nightly上,或者一旦数组上的try_map()方法稳定下来,你就可以访问一个更好的实现:

#![feature(array_try_map)]

impl TryFrom<(i16, i16, i16)> for Color {
    type Error = IntoColorError;
    fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
        let [red, green, blue] = <[_; 3]>::from(tuple).try_map(u8::try_from)?;
        Ok(Color { red, green, blue })
    }
}

这使用了在Rust 1.71版本中稳定的impl From<(T, T, T)> for [T; 3],以及上面提到的仍然不稳定的try_map()方法。
Playground
如果你想为Color添加更多的FromTryFrom实现,你可以将它们构建在彼此之上,并将每个impl中的代码减少到最少,例如:

impl TryFrom<(i16, i16, i16)> for Color {
    type Error = IntoColorError;
    fn try_from(rgb: (i16, i16, i16)) -> Result<Self, Self::Error> {
        <[_; 3]>::from(rgb).try_into()
    }
}
impl TryFrom<[i16; 3]> for Color {
    type Error = IntoColorError;
    fn try_from(rgb: [i16; 3]) -> Result<Self, Self::Error> {
        Ok(rgb.try_map(u8::try_from)?.into())
    }
}
impl From<[u8; 3]> for Color {
    fn from([red, green, blue]: [u8; 3]) -> Self {
        Color { red, green, blue }
    }
}
7eumitmz

7eumitmz2#

你可以将元组转换成数组,这样你就可以使用map

impl TryFrom<(i16, i16, i16)> for Color {
    type Error = String;
    fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
        let (red, green, blue) = tuple;
        let colors = [red, green, blue].map(u8::try_from);
        match colors {
            [Ok(red), Ok(green), Ok(blue)] => Ok(Color { red, green, blue }),
            _ => Err(Self::Error::IntConversion),
        }
    }
}

但是从一开始就为数组实现它可能会更干净。

impl TryFrom<[i16; 3]> for Color {
    type Error = String;
    fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {
        let colors = arr.map(u8::try_from);
        match colors {
            [Ok(red), Ok(green), Ok(blue)] => Ok(Color { red, green, blue }),
            _ => Err(Self::Error::IntConversion),
        }
    }
}

相关问题