rust中trait的使用方法详解

 更新时间:2023年12月25日 16:26:07   作者:王开源  
trait用中文来讲就是特征,它就是一个标记,只不过这个标记被用在特定的地方,也就是类型参数的后面,下面我们就来学习一下trait的具体使用方法吧
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

介绍

trait用中文来讲就是特征,它就是一个标记,只不过这个标记被用在特定的地方,也就是类型参数的后面,用来限定这个类型参数可能的类型范围。trait是一种约束。

具体关系为: 变量(值空间太过宽泛,添加约束) -> 类型(约束过死,放开约束) -> 泛型(类型空间太过宽泛,添加约束) -> trait

语法上,T: TraitA意思就是对类型参数T施加TraitA这个标记,具体实现为:

trait TraitA {}

struct Atype;

impl TraitA for Atype {}

对于某个类型T来说,如果它实现了这个TraitA,这个类型就满足约束

fn print<T: std::fmt::Display>(p: Point<T>){...}

上面这这段代码的意思是,Display对类型参数T做了约束,要求将来要带入的具体类型必须实现Display这个trait。也就是说,trait对类型参数施加约束的同时,也对具体的类型提供了能力,在Rust中约束和能力就是一体两面,是同一个东西。

trait中包含什么

trait里面可以包含关联函数、关联类型和关联常量。

关联函数

// 下列代码涉及所有权三态
trait Sport {
    fn play(&self) {} // 注意这里一对花括号,就是trait的关联函数的默认实现
    fn play_mut(&mut self);
    fn play_own(self);
    fn play_some() -> Self;
}

struct Football;
impl Sport for Football {
    // 由于play函数在trait中有关联函数的默认实现,结构体则可以不实现此函数
    fn play_mut(&mut self) {}
    fn play_own(self) {}
    fn play_some() -> Self { Self }
}

fn main() {
    let mut f = Football;
    f.play();
    f.play_mut();
    f.play_own();
    let _g = Football::play_some();
    let _g = <Football as Sport>::play_some(); // 等同于上一条代码
}

关联类型

在trait中,可以带一个或多个关联类型。关联类型起一个类型占位功能,定义trait时声明,在把trait实现到结构体上的时候为其指定具体的类型。

pub trait Sport {
    type ST; // 声明关联类型
    fn play(&self, st: Self::ST); // 将关联类型应用到关联函数
}

struct Football;
pub enum SportType {
    Land,
    Water,
}

impl Sport for Football {
    type ST = SportType; // 为关联类型指定具体类型
    fn play(&self, st: Self::ST){} // 方法中用到关联类型
}

fn main() {
    let f = Football;
    f.play(SportType::Land);
}

在T上使用关联类型

trait TraitA {
    type Mytype;
}

fn doit<T: TraitA>(a: T::Mytype) {} // 这里在函数中使用关联类型

struct TypeA;
impl TraitA for TypeA {
    type Mytype = String; // 具化关联类型为String
}

fn main() {
    doit::<TypeA>("abc".to_string()); // 指定泛型T为结构体TypeA
}

在约束中具化关联类型

trait TraitA {
    type Item;
}
// 意思就是限制x必须是实现TraitA而且它的关联类型Item必须是String的类型
struct Foo<T: TraitA<Item=String>> {
    x: T
}

对关联类型的约束

在定义关联类型的时候,也可以给关联类型添加约束。后面在具化这个类型的时候,那些类型必须要满足于这些约束

use std::fmt::Debug;

trait TraitA {
    type Item: Debug; // 这里对关联类型添加了Debug的约束
}

#[derive(Debug)]
struct A; // 这里在结构体A上自动derive Debug约束

struct B;

impl TraitA for B {
    type Item = A; // 这里类型A已经满足Debug约束
}

在使用时可以加强关联类型的约束

...
fn doit<T>(a: T)
where
    T: TriatA, // 约束T类型必须实现TraitA
    T::Item: Debug + PartialEq, // 同时约束trait的关联类型必须实现Debug和PartialEq
{
}

关联常量

和关联类型不同的是,关联常量可以在trait定义的时候指定,也可以在给具体类型实现的时候指定。

trait TraitA {
    const LEN: u32 = 10;
}

struct A;
impl TraitA from A {
    const LEN: u32 = 12;
}

where

当类型参数后面有对个trait约束的时候,会显得头重脚轻,所以Rust提供了where语法

fn doit<T: A + B + C + D + E + F>(t: T) -> i32 {}

fn doit<T>(t: T) -> u32
where
    T: A + B + C + D + E + F
{}

约束依赖

如果某种类型要实现TraitA,那么它也要同时实现TraitB。

trait TraitB {}
trait TraitA: TraitB {}
// 等价于
trait TraitC where Self: TraitB {}

约束之间是完全平等的,没有上下级关系

约束中同名方法的访问

trait Shape {
    fn play(&self) {
        println!("1");
    }
}
trait Circle: Shape {
    fn play(&self) {
        println!("2");
    }
}
struct A;
impl Shape for A {}
impl Circle for A {}
impl A {
    fn play(&self) {
        println!("3");
    }
}

fn main() {
    let a = A;
    a.play(); // 调用类型A上实现的play方法
    <A as Circle>::play(&a); // 调用trait Circle上的play方法
    <A as Shape>::play(&a); // 调用trait Shape上的play方法
}

这种语法叫做完全限定语法,是调用类型上某个方法的完整路径表达。

用trait实现能力配置

trait提供了寻找方法的范围

  • 检查有没有直接在这个类型上实现这个方法
  • 检查有没有在这个类型上实现某个trait,trait中有这个方法 一个类型可能实现了多个trait,不同的trait中各有一套方法,这些不同的方法中可能还会出现同名方法。Rust在这里采用了一种惰性的机制,由开发者指定在当前的mod或scope中使用哪套或哪几套能力。因此,对应地需要开发者手动地将要用到的trait引入当前scope。
mod module_a {
    pub trait Shape {
        fn play(&self) {
            println!("1");
        }
    }
    pub struct A;
    impl Shape for A {}
}
mod module_b {
    use supper::module_a::Shape; // 需要同时引入A用到的trait
    use super::module_a::A;
    
    fn doit() {
        let a = A;
        a.play();
    }
}

孤儿原则

为了不导致混乱,Rust要求在一个模块中,如果要对一个类型实现某个trait,这个类型和这个trait其中必须有一个是在当前模块定义的,如果必须用的话,可以用Newtyoe模式

Blanket Implementation

统一实现后,就不要对某个具体的类型再实现一次了,因为同一个trait只能实现一次到某个类型上。这个不像对类型做impl,可以实现多次(函数名要不冲突)。

trait TraitA {}
trait TraitB {}

impl<T: TraitB> TraitA for T {} // 为所有被TraitB约束的类型实现TraitA

impl TraitB for u32 {}
// impl TraitA for u32 {} // 无法再次实现

到此这篇关于rust中trait的使用方法详解的文章就介绍到这了,更多相关rust trait内容请搜索程序员之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持程序员之家!

相关文章

  • 为什么要使用 Rust 语言、Rust 语言有什么优势

    为什么要使用 Rust 语言、Rust 语言有什么优势

    虽然 Rust 是一种通用的多范式语言,但它的目标是 C 和 C++占主导地位的系统编程领域,很多朋友会问rust语言难学吗?rust语言可以做什么,今天带着这些疑问通过本文详细介绍下,感兴趣的朋友一起看看吧
    2022-10-10
  • Rust常用特型之Drop特型

    Rust常用特型之Drop特型

    本文主要介绍了Rust常用特型之Drop特型,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-03-03
  • 聊聊Rust 运算符

    聊聊Rust 运算符

    运算符 用于对数据执行一些操作。被运算符执行操作的数据我们称之为操作数。下面通过本文给大家介绍Rust 运算符的相关知识,感兴趣的朋友一起看看吧
    2021-11-11
  • rust开发环境配置详细教程

    rust开发环境配置详细教程

    rust是一门比较新的编程语言,2015年5月15日,Rust编程语言核心团队正式宣布发布Rust 1.0版本,这篇文章主要介绍了rust开发环境配置 ,需要的朋友可以参考下
    2022-12-12
  • 使用Cargo工具高效创建Rust项目

    使用Cargo工具高效创建Rust项目

    这篇文章主要介绍了使用Cargo工具高效创建Rust项目,本文有关Cargo工具的使用和Rust输入输出知识感兴趣的朋友一起看看吧
    2022-08-08
  • Rust语言从入门到精通系列之Iterator迭代器深入详解

    Rust语言从入门到精通系列之Iterator迭代器深入详解

    这篇文章主要为大家介绍了Rust语言从入门到精通系列之Iterator迭代器深入详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • 如何用Rust打印hello world

    如何用Rust打印hello world

    这篇文章主要介绍了如何用Rust打印hello world,本文分步骤通过图文并茂的形式给大家讲解的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-09-09
  • 如何使用VSCode配置Rust开发环境(Rust新手教程)

    如何使用VSCode配置Rust开发环境(Rust新手教程)

    这篇文章主要介绍了如何使用VSCode配置Rust开发环境(Rust新手教程),本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • 在Rust?web服务中使用Redis的方法

    在Rust?web服务中使用Redis的方法

    这篇文章主要介绍了在Rust?web服务中使用Redis,在这篇文章中,我们将演示如何在一个Rust?web应用程序中使用Redis,需要的朋友可以参考下
    2022-08-08
  • Rust 语言中的dyn 关键字及用途解析

    Rust 语言中的dyn 关键字及用途解析

    在Rust中,"dyn"关键字用于表示动态分发(dynamic dispatch),它通常与trait对象一起使用,以实现运行时多态, 在Rust中,多态是通过trait和impl来实现的,这篇文章主要介绍了Rust 语言中的 dyn 关键字,需要的朋友可以参考下
    2024-03-03

最新评论


http://www.vxiaotou.com