这是部分沙沙声
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
特征似乎是合理的。有什么方法可以实现这种行为吗?
您可以将元组转换为数组,以便您可以使用
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),
}
}
}
我认为在当前稳定的情况下,你所拥有的一切都很好。不过,在 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()
方法,该方法仍然不稳定。
(游乐场)