泛型编程与类型转换

一、泛型编程

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 保持哈希/比较一致性 需要统一借用类型的场景
注意事项
  1. Deref 不要滥用,避免隐式转换带来的理解困难
  2. 优先使用标准库的From/Into,而不是手写转换逻辑
  3. 涉及潜在失败的转换必须使用TryFrom/TryInto
  4. PhantomData主要解决类型系统占位问题,不影响运行时

Read more

Trait系统的细节:Sized、Dyn Trait、impl Trait、关联类型(Associated Types)

1. Sized:类型大小 底层原理 * 内存对齐:Sized 类型在栈上分配时,编译器必须知道其确切大小和对齐要求 * 类型系统守卫:Rust 默认要求泛型参数满足 T: Sized,因为编译器需要为泛型代码生成具体实现 * DST 的妥协:?Sized 放松约束,允许处理如 [T] 或 dyn Trait 等动态类型 深入示例 // 展示 Sized 的隐式约束 fn generic_fn<T>(t: T) {} // 实际等价于 fn generic_fn<T: Sized>(t: T) // 处理动态类型需要指针包装 fn unsized_types(

By amm

Rust所有权与生命周期底层机制

一、所有权系统(Ownership System) 1. 内存管理基础 * 基于栈(Stack)和堆(Heap)的差异: * 栈:自动管理,FILO结构,存储固定大小类型 * 堆:动态分配,存储不定大小类型,需要显式管理 * Rust 采用"谁创建谁负责释放"的原则,无垃圾回收机制 2. 所有权规则实现 * 每个值有且仅有一个所有者(Owner) * 当值被绑定到变量时,该变量成为值的所有者 * 当所有者离开作用域时,值会被自动释放(调用 drop trait) 3. 移动语义(Move Semantics) * 赋值操作默认执行移动而非拷贝: let s1 = String::from("hello"); let

By amm

© 2025 路不易All rights reserved.

备案号:黔ICP备2025043243号-1 | 公安备案图标 贵公网安备52052402000220号