上一篇的 From 只处理「一定成功」的转换。需要处理失败时用 TryFrom。这篇用对比表验证它与 as 的差别、怎么为自己的类型写带校验的转换、无损转换里那个看起来奇怪的 Infallible,以及切片转定长数组这个常见场景。
源码:
1 | use std::convert::TryFrom;
2 | fn main() {
3 | for v in [200i32, 300, -1, 255, 256] {
4 | println!("{:>4}:as u8 = {:>3},u8::try_from = {:?}", v, v as u8, u8::try_from(v));
5 | }
6 | println!("i32::try_from(3_000_000_000u64) = {:?}", i32::try_from(3_000_000_000u64));
7 | println!("u32::try_from(-5i64) = {:?}", u32::try_from(-5i64));
8 | let r: Result<u8, _> = 300i32.try_into();
9 | println!("300i32.try_into::<u8>() = {:?}", r);
10 | }
编译:成功
运行(退出码 0):
200:as u8 = 200,u8::try_from = Ok(200)
300:as u8 = 44,u8::try_from = Err(TryFromIntError(PosOverflow))
-1:as u8 = 255,u8::try_from = Err(TryFromIntError(NegOverflow))
255:as u8 = 255,u8::try_from = Ok(255)
256:as u8 = 0,u8::try_from = Err(TryFromIntError(PosOverflow))
i32::try_from(3_000_000_000u64) = Err(TryFromIntError(PosOverflow))
u32::try_from(-5i64) = Err(TryFromIntError(NegOverflow))
300i32.try_into::<u8>() = Err(TryFromIntError(PosOverflow))as 转换的更多规则见基本类型与类型推断。
源码:
1 | use std::convert::TryFrom;
2 | #[derive(Debug)] struct Port(u16);
3 | #[derive(Debug)] enum PortError { Negative, TooBig(i32), Reserved(u16) }
4 | impl TryFrom<i32> for Port {
5 | type Error = PortError;
6 | fn try_from(n: i32) -> Result<Self, PortError> {
7 | if n < 0 { return Err(PortError::Negative); }
8 | let p = u16::try_from(n).map_err(|_| PortError::TooBig(n))?;
9 | if p < 1024 { return Err(PortError::Reserved(p)); }
10 | Ok(Port(p))
11 | }
12 | }
13 | fn main() {
14 | for n in [8080, -1, 70000, 80] { println!("{:>6} → {:?}", n, Port::try_from(n)); }
15 | let p: Result<Port, _> = 443.try_into(); println!("443.try_into() → {:?}", p);
16 | }
编译:成功
运行(退出码 0):
8080 → Ok(Port(8080))
-1 → Err(Negative)
70000 → Err(TooBig(70000))
80 → Err(Reserved(80))
443.try_into() → Err(Reserved(443)) 源码:
1 | use std::convert::{Infallible, TryFrom};
2 | fn main() {
3 | let r: Result<u64, Infallible> = u64::try_from(5u32);
4 | println!("{:?}", r);
5 | match r { Ok(v) => println!("u32 → u64 的 try_from 一定成功:{v}"), Err(never) => match never {} }
6 | println!("size_of::<Infallible>() = {}", std::mem::size_of::<Infallible>());
7 | }
编译:成功
运行(退出码 0):
Ok(5)
u32 → u64 的 try_from 一定成功:5
size_of::<Infallible>() = 0无损转换的 From 见From、Into 与类型转换。
源码:
1 | use std::convert::TryInto;
2 | fn main() {
3 | let bytes = [0x12u8, 0x34, 0x56, 0x78, 0x9a];
4 | let a: [u8; 4] = bytes[..4].try_into().unwrap();
5 | println!("前 4 个字节 → [u8; 4]:{:02x?},大端 u32 = {:#x}", a, u32::from_be_bytes(a));
6 | let bad: Result<[u8; 4], _> = bytes[..].try_into();
7 | println!("5 个字节 → [u8; 4]:{:?}", bad);
8 | }
编译:成功
运行(退出码 0):
前 4 个字节 → [u8; 4]:[12, 34, 56, 78],大端 u32 = 0x12345678
5 个字节 → [u8; 4]:Err(TryFromSliceError(()))