Rust面向对象与Trait

简介: Rust面向对象与Trait
Rust中的Trait是比较新的概念,英文有“特质”、“特征”的意思,我们也可以简单将其看做TS中的interface

Trait(特质、特征)

Trait告诉Rust编译器:

  • 某种类型具有哪些并且可以与其他类型共享的功能

Trait:抽象的定义共享行为

Trait bounds(约束):泛型类型参数指定为实现了特定行为的类型

Trait与其他语言的接口(interface)类似,但也有区别。

定义一个Trait

Trait的定义:把方法签名放在一起,来定义实现某种目的所必须的一组行为。

  • 关键字:trait
  • 只有方法签名,没有具体实现
  • trait可以有多个方法:每个方法签名占一行,以;结尾
  • 实现该trait的类型必须提供具体的方法实现
pub trait CopyArticle {
    fn copy(&self) -> String;
    fn copy1(&self) -> String;
}

在类型上实现trait

与为类型实现方法类似。

不同之处:

  • impl Xxxx for Tweet{...} (Xxxx是trait名)
  • impl的块里,需要对Trait里的方法签名进行具体的实现

src/lib.rs

pub trait CopyArticle {
    fn copy(&self) -> String;
}

pub struct Jps {
    pub auther: String,
    pub title: String,
    pub content: String,
}

impl CopyArticle for Jps {
    fn copy(&self) -> String {
        format!(
            "title:{},content:{},auther:{}",
            self.title, self.content, self.auther
        )
    }
}

pub struct Book {
    pub auther: String,
    pub book_name: String,
}

impl CopyArticle for Book {
    fn copy(&self) -> String {
        format!("book_name is {},auther is {}", self.book_name, self.auther)
    }
}

src/main.rs

use panic::Book;//panic是Cargo.toml中的[package]的name
use panic::CopyArticle;
use panic::Jps;
fn main() {
    let book = Book {
        book_name: String::from("nice demo"),
        auther: String::from("Kevin"),
    };
    println!("{}", book.copy()); //book_name is nice demo,auther is Kevin
    let jps = Jps {
        title: String::from("demo_title"),
        content: String::from("this is a content"),
        auther: String::from("Kevin"),
    };
    println!("{}", jps.copy()); //title:demo_title,content:this is a content,auther:Kevin
}

我们在lib.rs中声明了trait和struct后在其他文件使用需要使用use导入。包名为Cargo.toml中的[package]的name

实现trait的约束

可以在某个类型上实现某个trait的前提条件是:

  • 这个类型或者trait是在本地create里定义的

我们无法为外部类型来实现外部的trait:

  • 这个限制是程序属性的一部分(也就是一致性
  • 更具体的说是孤儿原则:之所以这样命名是因为父类型不存在
  • 此规则确保其他人的代码不能破坏你的代码,反之亦然
  • 如果没有这个规则,两个create可以为同一类型实现同一个trait,Rust就不知道用哪个实现了

默认实现

我们可以在trait中默认实现方法,我们在上面的例子中做一些改变:

lib.rs

pub trait CopyArticle {
    // fn copy(&self) -> String;
    fn copy(&self) -> String {
        String::from("defualt")
    }
}
//...
impl CopyArticle for Jps {}
//...
impl CopyArticle for Book {
    //这里重写了copy方法
    fn copy(&self) -> String {
        format!("book_name is {},auther is {}", self.book_name, self.auther)
    }
}

main.rs

//...
println!("{}", jps.copy()); //defualt
//...
println!("{}", book.copy()); //book_name is nice demo,auther is Kevin

默认实现的方法可以调用trait中其他的方法,即使这些方法没有默认实现

pub trait CopyArticle {
    fn copy_dont(&self) -> String;
    fn copy(&self) -> String {
        format!("defualt,and more: {}", self.copy_dont())
    }
}
//...
impl CopyArticle for Jps {
    //需要实现没有默认实现的才能正常使用
    fn copy_dont(&self) -> String {
        format!(
            "title:{},content:{},auther:{}",
            self.title, self.content, self.auther
        )
    }
}

注意:无法从方法的重写实现里面调用默认的实现,比如这样:

pub trait CopyArticle {
    fn copy(&self) -> String {
        String::from("sss")
    }
}
//...
impl CopyArticle for Jps {
    fn copy(&self) -> String {
        format!("yes more: {}", self.copy())//不允许这样做
    }
}

Trait作为参数(类型)

比如我们现在要封装一个方法,在方法中要使用trait的方法,我们可以这样做:

使用impl Trait语法:适用于简单语法

pub trait CopyArticle {
    fn copy(&self) -> String;
}
//...
fn a_func(item: impl CopyArticle) {
    println!("{}", item.copy());
}

Trait bound语法(使用泛型):可用于复杂情况

//impl Trait语法
fn a_func1(item1: impl CopyArticle, item2: impl CopyArticle) {
    println!("{},{}", item1.copy(), item2.copy());
}

//Trait bound语法
fn a_func2<T: CopyArticle>(item1: T, item2: T) {
    println!("{},{}", item1.copy(), item2.copy());
}

实际上,impl Trait语法的Trait bound的语法糖

使用+指定多个Trait bound:

fn a_func1(item1: impl CopyArticle + Display) {
    println!("{}", item1.copy());
}

fn a_func2<T: CopyArticle + Display>(item1: T) {
    println!("{}", item1.copy());
}

我们指定多个Trait bound可能会出现函数签名过于累赘,影响代码的可读性,比如像这样:

fn a_func1<T: CopyArticle + Display, U: Debug + Clone>(item1: T, item2: U) -> String {
    format!("nice yes {}", item1.copy())
}

我们可以使用Trait bound使用where子句:

fn a_func2<T, U>(item1: T, item2: U) -> String
where
    T: CopyArticle + Display,
    U: Debug + Clone,
{
    format!("nice yes {}", item1.copy())
}

这个时候我们发现,函数签名的可读性就非常好了。

其中,where子句在函数签名的返回类型后。

实现Trait作为返回类型

使用impl Trait语法

fn a_func2() -> impl CopyArticle {
    Book {
        auther: String::from("kevin"),
        book_name: String::from("yes nice"),
    }
}

但是,需要注意的是:返回的类型必须确定的同一类型,返回可能不同的类型会报错

下面的代码将会报错,因为:虽然它们都实现了CopyArticle这个Trait,但是这个函数可能返回的类型是不同的类型,所以会报错。

//会报错
fn a_func2(boolean: bool) -> impl CopyArticle {
    if boolean {
        Book {
            auther: String::from("kevin"),
            book_name: String::from("yes nice"),
        }
    } else {
        Jps {
            auther: String::from("kevin"),
            title: String::from("yes nice"),
            content: String::from("ooo ooo ooo"),
        }
    }
}
相关文章
|
2月前
|
Rust
rust 引用了Trait的实现,为什么还需要引入Trait 才能调用实现的方法
rust 引用了Trait的实现,为什么还需要引入Trait 才能调用实现的方法
|
Rust 安全 Java
Rust学习笔记之泛型、trait 与生命周期
1. 泛型大补汤 推荐阅读指数 ⭐️⭐️⭐️⭐️ 2. 泛型数据类型 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️ 3. trait:定义共享的行为 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️ 4. 生命周期与引用有效性 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️
Rust学习笔记之泛型、trait 与生命周期
【Rust 中级教程】 11 所有权与trait(4)
【Rust 中级教程】 11 所有权与trait(4)
【Rust 中级教程】 11 所有权与trait(4)
|
Rust 编译器
【Rust 中级教程】 04 trait (3)
【Rust 中级教程】 04 trait (3)
【Rust 中级教程】 04 trait (3)
|
Rust Java
【Rust 中级教程】 04 trait (2)
【Rust 中级教程】 04 trait (2)
【Rust 中级教程】 04 trait (2)
|
Rust Java 编译器
【Rust 中级教程】 03 trait (1)
【Rust 中级教程】 03 trait (1)
【Rust 中级教程】 03 trait (1)
|
存储 Rust JavaScript
初识Rust中的面向对象
初识Rust中的面向对象
|
Rust 编译器 开发者
【Rust 中级教程】 06 trait (4)
【Rust 中级教程】 06 trait (4)
|
缓存 Rust Java
【Rust 课外知识】Rust中的三种多态性(中)——Trait的两种方式
【Rust 课外知识】Rust中的三种多态性(中)——Trait的两种方式
|
存储 Rust Java
【Rust 课外知识】Rust中的三种多态性(上)——Enum和Trait
【Rust 课外知识】Rust中的三种多态性(上)——Enum和Trait