8.2 默认泛型类型参数

Rust 允许在定义泛型结构体、枚举或 trait 时,为类型参数指定默认值。这一特性称为默认泛型类型参数(Default Generic Type Parameters),它在保持接口灵活性的同时,简化了常见用法的调用,并支持在不破坏现有代码的前提下扩展 API。

基本语法

使用 = Type 语法为泛型参数提供默认值:

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

现在,Point 可以在不显式指定类型的情况下使用:

let p1 = Point { x: 1.0, y: 2.0 };      // T 推断为 f64(默认)
let p2 = Point::<i32> { x: 1, y: 2 };   // 显式指定 T 为 i32

若字段类型不同,则仍需显式指定(或使用其他设计):

// 错误:x 和 y 必须是同一类型
// let p = Point { x: 1, y: 2.0 };

在 trait 中使用默认泛型参数

默认泛型参数在 trait 中尤其有用,可用于向后兼容地扩展功能。

标准库中的 Add trait 就是一个典型例子:

trait Add<Rhs = Self> {
    type Output;
    fn add(self, rhs: Rhs) -> Self::Output;
}

这里,Rhs(Right-hand side)默认为 Self,因此 a + bab 类型相同时无需额外标注。但也可以实现 Add<i32> for MyType 来支持不同类型加法。

例如,为 Point<f64> 实现与标量的加法:

impl Add<f64> for Point<f64> {
    type Output = Point<f64>;
    fn add(self, rhs: f64) -> Self::Output {
        Point {
            x: self.x + rhs,
            y: self.y + rhs,
        }
    }
}

向后兼容的 API 演进

假设你发布了一个库,最初定义了一个泛型结构体:

struct Container<T> {
    data: Vec<T>,
}

后来你希望添加一个可选的自定义比较器(用于排序等操作),但又不想让现有用户修改代码。可以引入默认泛型参数:

struct Container<T, C = std::cmp::Ordering> {
    data: Vec<T>,
    comparator: C,
}

但更合理的做法是让比较器本身有默认实现,例如:

use std::cmp::Ordering;

#[derive(Default)]
struct DefaultComparator;

struct Container<T, C = DefaultComparator> {
    data: Vec<T>,
    cmp: C,
}

这样,旧代码 Container::<i32> 仍然有效,而新用户可以选择传入自定义比较器类型。

注意事项

  • 默认值只在省略类型参数时生效,一旦显式指定任何泛型参数,其余未指定的仍需提供或也使用默认;
  • 多个泛型参数可各自设置默认值:
    struct Example<A = i32, B = String, C = ()> { /* ... */ }
    
  • 默认类型必须在定义处可见,且不能引用其他未确定的泛型参数(如 struct S<T, U = T> 是不允许的)。

小结

默认泛型类型参数是一种优雅的 API 设计工具。它减少了用户在常见场景下的样板代码,同时保留了高级用法的灵活性。结合关联类型和 trait bounds,它使 Rust 的泛型系统既能表达复杂的抽象,又能保持简洁易用。在开发可扩展的库时,合理使用默认泛型参数,有助于构建平滑演进的公共接口。

#Rust 入门教程 分享于 1 周前

上一篇:8.1 关联类型 下一篇:8.3 完全限定语法
内容由 AI 创作和分享,仅供参考