From 与 Into 是 Rust 里最常见的类型转换 trait。这篇验证它们的边界:标准库认为哪些转换是「无损」可以用 From、? 运算符怎么借 From 转换错误、写 impl Into
源码:
1 | use std::collections::HashMap;
2 | fn main() {
3 | let a = i64::from(7i32); // i32 → i64 无损
4 | let b = f64::from(3u8);
5 | let s = String::from("text"); let c = String::from('c');
6 | let v = Vec::from([1, 2, 3]); let bs: Vec<u8> = Vec::from("hi");
7 | let m = HashMap::from([("a", 1), ("b", 2)]);
8 | println!("{a} {b} {s} {c} {:?} {:?} 长度 {}", v, bs, m.len());
9 | }
编译:成功
运行(退出码 0):
7 3 text c [1, 2, 3] [104, 105] 长度 2
源码:
1 | fn main() { let x: i32 = i32::from(5i64); let y: u8 = u8::from(300u32); }
编译:失败
error[E0277]: the trait bound `i32: From<i64>` is not satisfied
--> <实验目录>/src/u49b.rs:1:26
|
1 | fn main() { let x: i32 = i32::from(5i64); let y: u8 = u8::from(300u32); }
| ^^^ the trait `From<i64>` is not implemented for `i32`
|
= help: `i32` implements trait `From<T>`:
From<bool>
From<i16>
From<i8>
From<u16>
From<u8>
error[E0277]: the trait bound `u8: From<u32>` is not satisfied
--> <实验目录>/src/u49b.rs:1:55
|
1 | fn main() { let x: i32 = i32::from(5i64); let y: u8 = u8::from(300u32); }
| ^^ the trait `From<u32>` is not implemented for `u8`
|
help: `u8` implements trait `From<T>`
--> <rust 标准库源码>/library/core/src/convert/num.rs:37:9
|
37 | const impl From<bool> for $int {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `From<bool>`
...
58 | impl_from_bool!(u8 u16 u32 u64 u128 usize);
…(共 40 行) 源码:
1 | use std::num::ParseIntError;
2 | use std::fmt;
3 | #[derive(Debug)] enum AppError { Parse(ParseIntError), Negative(i32) }
4 | impl From<ParseIntError> for AppError { fn from(e: ParseIntError) -> Self { AppError::Parse(e) } }
5 | impl fmt::Display for AppError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { AppError::Parse(e) => write!(f, "解析失败:{e}"), AppError::Negative(n) => write!(f, "{n} 是负数") } } }
6 | fn parse_positive(s: &str) -> Result<i32, AppError> {
7 | let n: i32 = s.parse()?; // ParseIntError 通过 From 自动变成 AppError
8 | if n < 0 { return Err(AppError::Negative(n)); }
9 | Ok(n)
10 | }
11 | fn main() {
12 | for s in ["42", "abc", "-5"] { match parse_positive(s) { Ok(n) => println!("{s} → Ok({n})"), Err(e) => println!("{s} → Err({e})") } }
13 | }
编译:成功
运行(退出码 0):
42 → Ok(42)
abc → Err(解析失败:invalid digit found in string)
-5 → Err(-5 是负数) 源码:
1 | use std::alloc::{GlobalAlloc, Layout, System};
2 | use std::sync::atomic::{AtomicUsize, Ordering};
3 | struct C; static AL: AtomicUsize = AtomicUsize::new(0);
4 | unsafe impl GlobalAlloc for C {
5 | unsafe fn alloc(&self, l: Layout) -> *mut u8 { AL.fetch_add(1, Ordering::Relaxed); unsafe { System.alloc(l) } }
6 | unsafe fn dealloc(&self, p: *mut u8, l: Layout) { unsafe { System.dealloc(p, l) } }
7 | }
8 | #[global_allocator] static G: C = C;
9 | struct User { name: String }
10 | fn new_user(name: impl Into<String>) -> User { User { name: name.into() } }
11 | fn main() {
12 | let owned = String::from("alice"); // 这次分配不计入下面的统计
13 | let a0 = AL.load(Ordering::Relaxed);
14 | let u1 = new_user("bob"); // &str → String:要分配
15 | let a1 = AL.load(Ordering::Relaxed);
16 | let u2 = new_user(owned); // String → String:Into 是恒等转换,直接移动
17 | let a2 = AL.load(Ordering::Relaxed);
18 | println!("传 &str:分配 {} 次;传 String:分配 {} 次", a1 - a0, a2 - a1);
19 | println!("{} {}", u1.name, u2.name);
20 | }
编译:成功
运行(退出码 0):
传 &str:分配 1 次;传 String:分配 0 次
bob alice有可能失败的转换见TryFrom 与失败转换。
源码:
1 | fn total<T: Into<f64>>(items: Vec<T>) -> f64 { items.into_iter().map(Into::into).sum() }
2 | fn main() { println!("{} {} {}", total(vec![1u8, 2]), total(vec![1.5f32, 2.5]), total(vec![2.5f64, 0.5])); }
编译:成功
运行(退出码 0):
3 4 3