Rust 中的泛型

数据类型是对同一类数据的抽象,而泛型是对具有一组相同行为的数据类型的抽象。Rust 使用 trait 来描述这一组相同的行为,简单的说 trait 就是一组函数的集合。Rust中的trait类似于其他语言中的常被称为接口(interfaces)的功能。

泛型

在函数中使用泛型

假如有两个函数,它们的功能是查找slice中最大值并返回,函数体中的采用的算法是一样的,主要不同的是参数类型。如下所示:

fn largest_i32(list: &[i32]) -> i32 {
    let mut largest = list[0];

    for &item in list.iter() {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn largest_char(list: &[char]) -> char {
    let mut largest = list[0];

    for &item in list.iter() {
        if item > largest {
            largest = item;
        }
    }

    largest
}

遇到这种情况我们就可以将重复部分抽取出来,用泛型代替参数中的数据类型。

// T 是泛型类型,它代表所有的数据类型。
fn largest<T>(list: &[T]) -> T {
    let mut largest = list[0];

    for &item in list.iter() {
        if item > largest {
            largest = item;
        }
    }

    largest
}
// 这样“任何”类型的`slice`都可以使用该函数了,而不用单独为每个类型都定义一个函数
fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    let result = largest(&number_list);
    println!("The largest number is {}", result);

    let char_list = vec!['y', 'm', 'a', 'q'];

    let result = largest(&char_list);
    println!("The largest char is {}", result);
}

但是,上面的示例是不能编译通过的,因为存在两个问题:

  • 函数体中的大于运算符(>)用于比较两个 T 类型的值,但并不是所有的数据类型都能使用这个运算符。
  • list参数的类型有可能是没有实现 Copy trait 的,这意味着我们可能不能将 list[0] 的值移动到 largest 变量中。

如果要完善这个函数,需要为T类型指定相应的trait,来进行限定。

结构体中使用泛型

可以使用 <> 语法来定义拥有一个或多个泛型参数类型字段的结构体。

示例一:指定一个泛型参数

struct Point<T> {
    x: T,
    y: T,
}
// x 和 y 的类型必须相同
fn main() {
    let integer = Point { x: 5, y: 10 };
    let float = Point { x: 1.0, y: 4.0 };
}

示例二:指定两个泛型参数

struct Point<T, U> {
    x: T,
    y: U,
}
// x 和 y 的类型可以不同
fn main() {
    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 };
}

枚举中使用泛型

与结构体类似,枚举中也可以使用泛型。

示例一:标准库中的Option<T>

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

示例二:标准库中的Result<T, E>

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

方法定义中使用泛型

示例:在 Point<T> 结构体上实现方法 x,它返回 T 类型的字段 x 的引用

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()
    }
}

trait

使用trait可以帮助我们确保类型拥有期望的行为,对数据类型进行限定。

定义 trait

使用 trait关键字定义 trait

示例

pub trait Summary {
    // 可以有多个方法:一行一个方法签名且都以分号结尾。
    fn summarize(&self) -> String;
}

实现 trait

示例

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)
    }
}

默认实现

trait中可以提供默认的实现,当某个特定的类型实现trait时,可以选择保留或者重载这个默认的实现。

示例

// Summary trait 的定义,带有一个 summarize 方法的默认实现
pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(Read more...)")
    }
}
// NewsArticle 类型使用这个默认实现
impl Summary for NewsArticle {}
// 重载这个默认实现
impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}, by {} ({})", self.headline, self.author, self.location)
    }
}

trait 作为参数

trait代表的是一组函数,因此可以用它来指定那些实现了特定trait的数据类型。

示例一

// 任何实现了 Summary 的类型都可以使用这个函数
// 可以认为是在泛型的基础上,添加了 trait 限定。这种语法称之为 trait bound。
pub fn notify<T: Summary>(item: T) {
    println!("Breaking news! {}", item.summarize());
}
// 这是对上面语法形式的简写
pub fn notify(item: impl Summary) {
    println!("Breaking news! {}", item.summarize());
}

示例二:通过 + 指定多个 trait,数据类型必须实现指定的多个 trait

// 使用 impl 关键字
pub fn notify(item: impl Summary + Display) {}
// 使用 trait bound 语法
pub fn notify<T: Summary + Display>(item: T) {}

示例三:通过 where 简化 trait bound

fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 {}
// 上面的函数可以简化成下面这种形式
fn some_function<T, U>(t: T, u: U) -> i32
    where T: Display + Clone,
              U: Clone + Debug
{}

示例四:返回实现了 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,
    }
}

相关资料

The Rust Programming Language

Rust by Example

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值