泛型编程与类型转换
一、泛型编程
1. 泛型约束(where
从句)
作用:为泛型参数添加约束条件,限制可接受的类型范围。
use std::fmt::Display;
// 泛型约束的两种写法
fn print<T: Display>(item: T) {
println!("{}", item);
}
fn print_where<T>(item: T)
where
T: Display,
{
println!("{}", item);
}
struct Pair<T> {
a: T,
b: T,
}
// 为实现了Display + PartialOrd的类型实现方法
impl<T> Pair<T>
where
T: Display + PartialOrd,
{
fn cmp_display(&self) {
if self.a >= self.b {
println!("a is larger: {}", self.a);
} else {
println!("b is larger: {}", self.b);
}
}
}
fn main() {
print(42); // 正确,i32实现了Display
print_where("hello"); // 正确,&str实现了Display
let pair = Pair { a: 8, b: 5 };
pair.cmp_display(); // 输出:a is larger: 8
}
2. PhantomData(幽灵数据)
作用:标记未使用的类型参数或生命周期参数,常用于FFI或类型系统占位。
use std::marker::PhantomData;
// 模拟拥有泛型类型T的指针,但不实际存储T类型数据
struct MyContainer<T> {
data: *const (),
_marker: PhantomData<T>,
}
impl<T> MyContainer<T> {
fn new() -> Self {
Self {
data: std::ptr::null(),
_marker: PhantomData,
}
}
}
// 生命周期标记示例
struct Iter<'a, T> {
ptr: *const T,
end: *const T,
_marker: PhantomData<&'a T>,
}
fn main() {
let _container: MyContainer<String> = MyContainer::new();
}
3. 泛型生命周期参数
作用:处理跨引用的生命周期关系,确保引用有效性。
struct Book<'a> {
title: &'a str,
author: &'a str,
}
// 泛型生命周期在结构体中的使用
impl<'a> Book<'a> {
fn new(title: &'a str, author: &'a str) -> Self {
Book { title, author }
}
}
// 函数中的生命周期参数
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
fn main() {
let novel = String::from("Rust Programming");
let first_sentence = novel.split('.').next().unwrap();
let book = Book::new("Rust Book", "John Doe");
let s1 = "abcd";
let s2 = "xyz";
let longest_str = longest(s1, s2);
println!("Longest: {}", longest_str);
}
二、Trait高级技巧
1. Deref/DerefMut
作用:实现智能指针的自动解引用功能。
use std::ops::{Deref, DerefMut};
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for MyBox<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
fn print_len(s: &str) {
println!("Length: {}", s.len());
}
fn main() {
let mut s = MyBox::new(String::from("Rust"));
print_len(&s); // 自动调用deref:&MyBox -> &String -> &str
*s = String::from("New Value"); // DerefMut允许修改
println!("{}", *s);
}
2. Borrow/BorrowMut
作用:提供统一的借用接口,支持不同类型但具有相同哈希/比较行为的借用。
use std::borrow::Borrow;
fn check_hash<T: Borrow<str>>(s: T) {
let s_ref: &str = s.borrow();
println!("Hash: {}", s_ref.len());
}
fn main() {
let s1 = "hello";
let s2 = String::from("world");
check_hash(s1);
check_hash(&s2); // 自动转换为&str
}
3. From/Into
作用:类型转换的通用接口,零成本抽象。
struct Celsius(f64);
struct Fahrenheit(f64);
impl From<Celsius> for Fahrenheit {
fn from(c: Celsius) -> Self {
Fahrenheit(c.0 * 1.8 + 32.0)
}
}
fn main() {
let c = Celsius(100.0);
let f: Fahrenheit = c.into(); // 自动调用From实现
println!("{}°C = {}°F", c.0, f.0);
// 使用Into trait直接转换
let f2 = Fahrenheit::from(c);
let c2: Celsius = Celsius::from(Fahrenheit(212.0));
}
三、类型转换
1. as
运算符
适用场景:基本类型转换、指针转换、强制缩短生命周期。
fn main() {
// 基本类型转换
let x = 42u32 as u64;
let y = 3.14 as f32;
// 指针转换
let ptr = &42 as *const i32 as usize;
// 枚举到整数
enum Color { Red = 0xff0000 }
println!("Red: {}", Color::Red as i32);
}
2. From/Into
适用场景:无错误的显式类型转换。
struct Millimeters(u32);
struct Meters(u32);
impl From<Meters> for Millimeters {
fn from(m: Meters) -> Self {
Millimeters(m.0 * 1000)
}
}
fn main() {
let meters = Meters(2);
let millimeters: Millimeters = meters.into();
println!("{} meters = {} millimeters", meters.0, millimeters.0);
}
3. TryFrom/TryInto
适用场景:可能失败的转换(返回Result类型)。
use std::convert::TryFrom;
struct EvenNumber(i32);
impl TryFrom<i32> for EvenNumber {
type Error = String;
fn try_from(value: i32) -> Result<Self, Self::Error> {
if value % 2 == 0 {
Ok(EvenNumber(value))
} else {
Err("Value is not even".to_string())
}
}
}
fn main() {
match EvenNumber::try_from(8) {
Ok(num) => println!("Even: {}", num.0),
Err(e) => println!("Error: {}", e),
}
let result: Result<EvenNumber, _> = 5i32.try_into();
assert!(result.is_err());
}
四、对比总结
转换方式 | 特点 | 使用场景 |
---|---|---|
as |
强制转换,可能丢失精度 | 基础类型转换、指针转换 |
From/Into |
无错误转换,需显式实现 | 自定义类型的安全转换 |
TryFrom/TryInto |
可能失败,返回Result | 需要错误处理的转换 |
Deref |
自动解引用,支持方法调用链 | 智能指针实现 |
Borrow |
保持哈希/比较一致性 | 需要统一借用类型的场景 |
注意事项: |
Deref
不要滥用,避免隐式转换带来的理解困难- 优先使用标准库的From/Into,而不是手写转换逻辑
- 涉及潜在失败的转换必须使用TryFrom/TryInto
- PhantomData主要解决类型系统占位问题,不影响运行时