03-错误处理、泛型与Trait

Rust错误处理显式明确,泛型实现零成本抽象,Trait定义共享行为。

错误处理

Rust无异常机制,错误分两类:可恢复(Result<T, E>)和不可恢复(panic!)。

panic!

不可恢复错误,程序终止。

// 主动panic
panic!("crash and burn");

// 数组越界等会panic
let v = vec![1, 2, 3];
v[99];  // panic!

// panic时的行为
// 1. 展开(unwind):清理栈(默认)
// 2. 中止(abort):直接终止,OS清理

// Cargo.toml设置:
// [profile.release]
// panic = 'abort'

Result<T, E>

可恢复错误。

enum Result<T, E> {
    Ok(T),
    Err(E),
}

use std::fs::File;

// 基本使用
let f = File::open("hello.txt");

let f = match f {
    Ok(file) => file,
    Err(error) => {
        panic!("打开文件失败: {:?}", error);
    },
};

// 匹配不同错误
use std::io::ErrorKind;

let f = File::open("hello.txt");

let f = match f {
    Ok(file) => file,
    Err(error) => match error.kind() {
        ErrorKind::NotFound => match File::create("hello.txt") {
            Ok(fc) => fc,
            Err(e) => panic!("创建文件失败: {:?}", e),
        },
        other_error => panic!("打开文件失败: {:?}", other_error),
    },
};

// 简化版(闭包)
let f = File::open("hello.txt").unwrap_or_else(|error| {
    if error.kind() == ErrorKind::NotFound {
        File::create("hello.txt").unwrap_or_else(|error| {
            panic!("创建文件失败: {:?}", error);
        })
    } else {
        panic!("打开文件失败: {:?}", error);
    }
});

unwrap和expect

// unwrap:Ok返回值,Err则panic
let f = File::open("hello.txt").unwrap();

// expect:自定义panic消息
let f = File::open("hello.txt").expect("无法打开hello.txt");

传播错误

use std::io;
use std::fs::File;
use std::io::Read;

// 手动传播
fn read_username_from_file() -> Result<String, io::Error> {
    let f = File::open("hello.txt");

    let mut f = match f {
        Ok(file) => file,
        Err(e) => return Err(e),
    };

    let mut s = String::new();

    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}

// ? 运算符(简化)
fn read_username_from_file() -> Result<String, io::Error> {
    let mut f = File::open("hello.txt")?;  // 错误自动返回
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

// 链式调用
fn read_username_from_file() -> Result<String, io::Error> {
    let mut s = String::new();
    File::open("hello.txt")?.read_to_string(&mut s)?;
    Ok(s)
}

// 更简洁
fn read_username_from_file() -> Result<String, io::Error> {
    std::fs::read_to_string("hello.txt")
}

// ? 用于Option
fn last_char_of_first_line(text: &str) -> Option<char> {
    text.lines().next()?.chars().last()
}

自定义错误类型

use std::fmt;

#[derive(Debug)]
enum MyError {
    Io(std::io::Error),
    Parse(std::num::ParseIntError),
}

impl fmt::Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            MyError::Io(err) => write!(f, "IO错误: {}", err),
            MyError::Parse(err) => write!(f, "解析错误: {}", err),
        }
    }
}

impl std::error::Error for MyError {}

// From trait自动转换
impl From<std::io::Error> for MyError {
    fn from(err: std::io::Error) -> Self {
        MyError::Io(err)
    }
}

impl From<std::num::ParseIntError> for MyError {
    fn from(err: std::num::ParseIntError) -> Self {
        MyError::Parse(err)
    }
}

// 使用
fn process() -> Result<i32, MyError> {
    let file_content = std::fs::read_to_string("number.txt")?;  // IO错误自动转换
    let number: i32 = file_content.trim().parse()?;  // 解析错误自动转换
    Ok(number)
}

anyhow和thiserror

// anyhow:应用程序错误(简单)
use anyhow::{Context, Result};

fn read_config() -> Result<String> {
    let content = std::fs::read_to_string("config.toml")
        .context("读取配置文件失败")?;
    Ok(content)
}

// thiserror:库错误(定义错误类型)
use thiserror::Error;

#[derive(Error, Debug)]
enum DataError {
    #[error("IO错误: {0}")]
    Io(#[from] std::io::Error),
    
    #[error("解析错误: {0}")]
    Parse(#[from] std::num::ParseIntError),
    
    #[error("无效数据: {0}")]
    Invalid(String),
}

泛型

函数泛型

// 找最大值(具体类型)
fn largest_i32(list: &[i32]) -> i32 {
    let mut largest = list[0];
    for &item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}

// 泛型版本
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
    let mut largest = list[0];
    for &item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}

let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);

let char_list = vec!['y', 'm', 'a', 'q'];
let result = largest(&char_list);

结构体泛型

struct Point<T> {
    x: T,
    y: T,
}

let integer = Point { x: 5, y: 10 };
let float = Point { x: 1.0, y: 4.0 };

// 多个类型参数
struct Point<T, U> {
    x: T,
    y: U,
}

let both_integer = Point { x: 5, y: 10 };
let both_float = Point { x: 1.0, y: 4.0 };
let integer_and_float = Point { x: 5, y: 4.0 };

枚举泛型

enum Option<T> {
    Some(T),
    None,
}

enum Result<T, E> {
    Ok(T),
    Err(E),
}

方法泛型

struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

// 只为特定类型实现
impl Point<f32> {
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

// 方法上的额外泛型
impl<T, U> Point<T, U> {
    fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}

let p1 = Point { x: 5, y: 10.4 };
let p2 = Point { x: "Hello", y: 'c' };
let p3 = p1.mixup(p2);  // Point { x: 5, y: 'c' }

零成本抽象

泛型编译期单态化(monomorphization),运行时无开销。

// 泛型代码
let integer = Some(5);
let float = Some(5.0);

// 编译后等效于
enum Option_i32 {
    Some(i32),
    None,
}

enum Option_f64 {
    Some(f64),
    None,
}

let integer = Option_i32::Some(5);
let float = Option_f64::Some(5.0);

Trait

定义共享行为,类似接口。

定义和实现

pub trait Summary {
    fn summarize(&self) -> String;
}

pub struct NewsArticle {
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}

impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}, by {} ({})", self.headline, self.author, self.location)
    }
}

pub struct Tweet {
    pub username: String,
    pub content: String,
    pub reply: bool,
    pub retweet: bool,
}

impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

// 使用
let tweet = Tweet {
    username: String::from("horse_ebooks"),
    content: String::from("of course, as you probably already know, people"),
    reply: false,
    retweet: false,
};

println!("1 new tweet: {}", tweet.summarize());

默认实现

pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(Read more...)")  // 默认实现
    }
}

// 使用默认实现
impl Summary for NewsArticle {}

let article = NewsArticle { /* ... */ };
println!("{}", article.summarize());  // "(Read more...)"

// 默认实现可调用其他方法
pub trait Summary {
    fn summarize_author(&self) -> String;

    fn summarize(&self) -> String {
        format!("(Read more from {}...)", self.summarize_author())
    }
}

Trait作为参数

// Trait bound语法
pub fn notify<T: Summary>(item: &T) {
    println!("Breaking news! {}", item.summarize());
}

// impl Trait语法糖
pub fn notify(item: &impl Summary) {
    println!("Breaking news! {}", item.summarize());
}

// 多个trait
pub fn notify(item: &(impl Summary + Display)) {
    // ...
}

pub fn notify<T: Summary + Display>(item: &T) {
    // ...
}

// where子句(复杂约束)
fn some_function<T, U>(t: &T, u: &U) -> i32
    where T: Display + Clone,
          U: Clone + Debug
{
    // ...
}

返回Trait

// 返回实现了trait的类型
fn returns_summarizable() -> impl Summary {
    Tweet {
        username: String::from("horse_ebooks"),
        content: String::from("of course, as you probably already know, people"),
        reply: false,
        retweet: false,
    }
}

// ❌ 错误:不能返回不同类型
fn returns_summarizable(switch: bool) -> impl Summary {
    if switch {
        NewsArticle { /* ... */ }
    } else {
        Tweet { /* ... */ }  // 错误:类型不一致
    }
}

有条件的实现

use std::fmt::Display;

struct Pair<T> {
    x: T,
    y: T,
}

impl<T> Pair<T> {
    fn new(x: T, y: T) -> Self {
        Self { x, y }
    }
}

// 只为实现了Display + PartialOrd的类型实现
impl<T: Display + PartialOrd> Pair<T> {
    fn cmp_display(&self) {
        if self.x >= self.y {
            println!("最大值是 x = {}", self.x);
        } else {
            println!("最大值是 y = {}", self.y);
        }
    }
}

Blanket实现

// 为所有实现了Display的类型实现ToString
impl<T: Display> ToString for T {
    // ...
}

// 因此任何Display类型都有to_string方法
let s = 3.to_string();

常用Trait

// Clone:深拷贝
#[derive(Clone)]
struct MyStruct;

// Copy:栈拷贝(只能是简单类型)
#[derive(Copy, Clone)]
struct Point { x: i32, y: i32 }

// Debug:调试输出
#[derive(Debug)]
struct Rectangle { width: u32, height: u32 }
println!("{:?}", rect);

// Display:用户友好输出
use std::fmt;

impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

// PartialEq, Eq:相等比较
#[derive(PartialEq, Eq)]
struct Person { name: String }

// PartialOrd, Ord:排序
#[derive(PartialOrd, Ord)]
struct Priority(i32);

// Default:默认值
#[derive(Default)]
struct Config {
    name: String,
    count: i32,
}
let config = Config::default();

// From/Into:类型转换
impl From<i32> for MyType {
    fn from(n: i32) -> Self {
        MyType(n)
    }
}

let my_type: MyType = 5.into();

生命周期

确保引用有效,编译期检查。

基本语法

// 函数签名中的生命周期
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

// 使用
let string1 = String::from("abcd");
let string2 = "xyz";

let result = longest(string1.as_str(), string2);
println!("最长的字符串是 {}", result);

// 生命周期含义:
// 返回值的生命周期与参数中较短的相同

结构体生命周期

struct ImportantExcerpt<'a> {
    part: &'a str,
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("Could not find a '.'");
    let i = ImportantExcerpt { part: first_sentence };
}

生命周期省略规则

编译器自动推断生命周期的规则:

// 规则1:每个引用参数都有独立生命周期
fn foo<'a, 'b>(x: &'a i32, y: &'b i32)

// 规则2:只有一个输入生命周期,赋给所有输出
fn foo<'a>(x: &'a i32) -> &'a i32

// 规则3:方法中,返回值生命周期赋给&self
impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("Attention please: {}", announcement);
        self.part  // 返回值生命周期等于&self
    }
}

静态生命周期

// 'static:整个程序运行期间
let s: &'static str = "I have a static lifetime.";

// 所有字符串字面量都是'static

高级Trait

关联类型

pub trait Iterator {
    type Item;  // 关联类型

    fn next(&mut self) -> Option<Self::Item>;
}

impl Iterator for Counter {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        // ...
    }
}

// vs 泛型
pub trait Iterator<T> {
    fn next(&mut self) -> Option<T>;
}

// 关联类型:只能有一个实现
// 泛型:可以有多个实现(如Iterator<String>和Iterator<i32>)

运算符重载

use std::ops::Add;

#[derive(Debug, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

impl Add for Point {
    type Output = Point;

    fn add(self, other: Point) -> Point {
        Point {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

assert_eq!(
    Point { x: 1, y: 0 } + Point { x: 2, y: 3 },
    Point { x: 3, y: 3 }
);

Deref trait

use std::ops::Deref;

struct MyBox<T>(T);

impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

let x = 5;
let y = MyBox(x);

assert_eq!(5, *y);  // *y -> *(y.deref())

Drop trait

struct CustomSmartPointer {
    data: String,
}

impl Drop for CustomSmartPointer {
    fn drop(&mut self) {
        println!("Dropping CustomSmartPointer with data `{}`!", self.data);
    }
}

fn main() {
    let c = CustomSmartPointer { data: String::from("my stuff") };
    let d = CustomSmartPointer { data: String::from("other stuff") };
    println!("创建了CustomSmartPointers");
}  // 离开作用域,先drop d,再drop c

// 提前drop
drop(c);  // 显式调用std::mem::drop

最佳实践

错误处理

// ✓ 库:返回Result,让调用者决定
pub fn read_file(path: &str) -> Result<String, io::Error> {
    std::fs::read_to_string(path)
}

// ✓ 应用:合适位置处理错误
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let content = read_file("config.toml")?;
    // ...
    Ok(())
}

// ❌ 避免:过度unwrap
let file = File::open("file.txt").unwrap();  // 生产代码少用

// ✓ 合理panic:逻辑错误、不可恢复错误
if index > MAX_INDEX {
    panic!("索引越界: {}", index);
}

泛型使用

// ✓ 简洁约束
fn print<T: Display>(t: T) {
    println!("{}", t);
}

// ✓ 复杂约束用where
fn complex<T, U>(t: T, u: U)
where
    T: Display + Clone,
    U: Debug + Clone,
{
    // ...
}

Trait设计

// ✓ 小而专注
trait Read {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
}

// ✓ 提供默认实现
trait Logger {
    fn log(&self, message: &str) {
        println!("[LOG] {}", message);
    }
}

// ✓ 使用关联类型简化
trait Container {
    type Item;
    fn get(&self, index: usize) -> Option<&Self::Item>;
}

错误处理高级技巧

错误上下文

use anyhow::{Context, Result};

fn read_config() -> Result<String> {
    std::fs::read_to_string("config.toml")
        .context("读取配置文件失败")?;
    
    // 多层上下文
    let content = std::fs::read_to_string("data.json")
        .context("读取数据文件失败")
        .context("初始化应用失败")?;
    
    Ok(content)
}

// 错误链
// Error: 初始化应用失败
// Caused by:
//     0: 读取数据文件失败
//     1: No such file or directory

自定义Result类型

// 定义别名
type Result<T> = std::result::Result<T, MyError>;

// 函数简化
fn process() -> Result<String> {  // 不需要写MyError
    Ok(String::from("done"))
}

// 标准库示例:std::io::Result<T> = Result<T, std::io::Error>

Option和Result转换

// Option转Result
let opt: Option<i32> = Some(5);
let res: Result<i32, &str> = opt.ok_or("没有值");

// Result转Option
let res: Result<i32, _> = Ok(5);
let opt = res.ok();  // Some(5)

// 链式转换
let value = std::env::var("PORT")  // Result<String, VarError>
    .ok()  // Option<String>
    .and_then(|s| s.parse().ok())  // Option<u16>
    .unwrap_or(8080);

提前返回模式

fn validate_user(user: &User) -> Result<()> {
    // 提前返回错误
    if user.name.is_empty() {
        return Err(Error::EmptyName);
    }
    
    if user.age < 18 {
        return Err(Error::TooYoung);
    }
    
    if !user.email.contains('@') {
        return Err(Error::InvalidEmail);
    }
    
    Ok(())  // 所有检查通过
}

// 使用?简化
fn validate_user(user: &User) -> Result<()> {
    ensure!(!user.name.is_empty(), "名字不能为空");
    ensure!(user.age >= 18, "年龄必须>=18");
    ensure!(user.email.contains('@'), "邮箱格式错误");
    Ok(())
}

Trait高级应用

Trait Object动态分发

trait Draw {
    fn draw(&self);
}

struct Circle;
struct Rectangle;

impl Draw for Circle {
    fn draw(&self) { println!("Circle"); }
}

impl Draw for Rectangle {
    fn draw(&self) { println!("Rectangle"); }
}

// 静态分发(编译期确定)
fn draw_static<T: Draw>(shape: &T) {
    shape.draw();
}

// 动态分发(运行时确定)
fn draw_dynamic(shape: &dyn Draw) {
    shape.draw();
}

// Trait Object集合
let shapes: Vec<Box<dyn Draw>> = vec![
    Box::new(Circle),
    Box::new(Rectangle),
];

for shape in shapes {
    shape.draw();  // 运行时多态
}

孤儿规则和newtype模式

// ❌ 错误:不能为外部类型实现外部trait
// impl ToString for Vec<i32> { }  // 编译错误

// ✅ newtype模式绕过孤儿规则
struct Wrapper(Vec<i32>);

impl ToString for Wrapper {
    fn to_string(&self) -> String {
        format!("{:?}", self.0)
    }
}

// Deref自动解引用
use std::ops::Deref;

impl Deref for Wrapper {
    type Target = Vec<i32>;
    
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

let w = Wrapper(vec![1, 2, 3]);
println!("{}", w.len());  // 自动解引用到Vec

Trait边界技巧

// where子句分离
fn complex_function<T, U>(t: T, u: U)
where
    T: Clone + Debug + PartialEq,
    U: Clone + Debug,
{
    // ...
}

// impl Trait(参数)
fn notify(item: &impl Summary) {
    // 等价于:fn notify<T: Summary>(item: &T)
}

// impl Trait(返回值)
fn returns_closure() -> impl Fn(i32) -> i32 {
    |x| x + 1
}

// 条件编译和Trait
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
struct MyStruct {
    field: String,
}

关联类型 vs 泛型

// 关联类型:每个类型只有一个实现
trait Container {
    type Item;
    fn get(&self) -> Option<&Self::Item>;
}

impl Container for Vec<i32> {
    type Item = i32;  // 只能实现一次
    fn get(&self) -> Option<&i32> {
        self.first()
    }
}

// 泛型:可以有多个实现
trait Add<RHS = Self> {
    type Output;
    fn add(self, rhs: RHS) -> Self::Output;
}

impl Add<i32> for i32 {
    type Output = i32;
    fn add(self, rhs: i32) -> i32 {
        self + rhs
    }
}

impl Add<f32> for i32 {
    type Output = f32;
    fn add(self, rhs: f32) -> f32 {
        self as f32 + rhs
    }
}

Trait继承

trait Animal {
    fn name(&self) -> &str;
}

trait Dog: Animal {  // Dog继承Animal
    fn bark(&self);
}

struct GoldenRetriever {
    name: String,
}

impl Animal for GoldenRetriever {
    fn name(&self) -> &str {
        &self.name
    }
}

impl Dog for GoldenRetriever {
    fn bark(&self) {
        println!("{} says woof!", self.name());
    }
}

// 使用
fn pet_dog(dog: &impl Dog) {
    println!("Petting {}", dog.name());  // 可以调用Animal的方法
    dog.bark();
}

Trait别名(nightly)

#![feature(trait_alias)]

// 定义trait别名
trait MyTrait = Clone + Debug + Send;

fn process<T: MyTrait>(value: T) {
    // T必须实现Clone、Debug、Send
}

完全限定语法

trait Pilot {
    fn fly(&self);
}

trait Wizard {
    fn fly(&self);
}

struct Human;

impl Pilot for Human {
    fn fly(&self) {
        println!("This is your captain speaking.");
    }
}

impl Wizard for Human {
    fn fly(&self) {
        println!("Up!");
    }
}

impl Human {
    fn fly(&self) {
        println!("*waving arms furiously*");
    }
}

let person = Human;
person.fly();  // 调用Human::fly

Pilot::fly(&person);  // 调用Pilot::fly
Wizard::fly(&person);  // 调用Wizard::fly

// 关联函数(无self)
<Human as Pilot>::fly(&person);  // 完全限定语法

泛型性能分析

单态化示例

// 泛型代码
fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}

// 编译后生成两个具体版本
fn largest_i32(list: &[i32]) -> &i32 {
    // ...
}

fn largest_char(list: &[char]) -> &char {
    // ...
}

// 调用
let numbers = vec![34, 50, 25];
let result = largest(&numbers);  // 调用largest_i32

let chars = vec!['y', 'm', 'a'];
let result = largest(&chars);  // 调用largest_char

代码膨胀问题

// ❌ 过度泛型导致代码膨胀
fn process<T: Clone>(a: T, b: T, c: T, d: T) {
    // 每个类型都生成一份代码
}

// ✅ 使用trait object减少膨胀
fn process(items: &[&dyn Clone]) {
    // 只有一份代码,运行时分发
}

// 平衡:常用类型静态分发,少用类型动态分发

核心: 错误处理显式、泛型零成本抽象、Trait提供代码复用和多态,是Rust类型系统的核心特性。