训练的decoder模型文本长度不一致,一般设置为多大合适,需要覆盖最长的文本长度么

阅读 16

2024-11-28

文章目录


Rust的trait是一种对类型行为的抽象,类似于其他语言中的接口(如Java的interface或C++的abstract class)。它定义了一组方法,可以被多种类型实现,从而允许在 Rust中实现多态。

trait主要用途

1.定义共享的行为
trait允许不同的类型共享相同的行为。可以定义一个trait来指定某个方法或一组方法,然后多个类型可以实现这个trait。

2.泛型约束
在使用泛型时,trait可以作为约束条件,指定某个泛型类型必须实现特定的trait,这样就能在泛型函数中安全地调用trait中定义的方法。

3.通过trait对象实现动态分发
Rust支持通过trait对象来进行动态方法调用,类似于其他语言中的动态类型。这允许在运行时决定调用哪个对象的哪个方法。

特征调用示例:

//定义共享行为  
trait Animal {
    fn speak(&self);
}

struct Dog;
impl Animal for Dog {
    fn speak(&self) {
        println!("Woof!");
    }
}

struct Cat;
impl Animal for Cat {
    fn speak(&self) {
        println!("Meow!");
    }
}

// 泛型函数,接受实现了Animal特征的任何类型  
fn make_animal_speak<T: Animal>(animal: T) {
    animal.speak();
}

fn main() {
    let my_dog = Dog;
    let my_cat = Cat;

    // 调用泛型函数
    make_animal_speak(my_dog);
    make_animal_speak(my_cat);
}

特征的基本用法

默认方法实现

在Rust中trait可以提供默认的方法实现。这意味着实现该trait的类型可以使用或覆盖这些默认方法。这种机制允许更灵活的代码重用和扩展。

trait Animal {
    fn speak(&self) {
        println!("This animal makes a sound.");
    }
}
struct Dog;
struct Cat;
impl Animal for Dog {
    // 使用默认实现,不需要再次实现 `speak` 方法
}
impl Animal for Cat {
    // 覆盖默认实现
    fn speak(&self) {
        println!("Meow!");
    }
}

//使用
fn main() {
    let dog = Dog;
    let cat = Cat;
    dog.speak(); // 输出 "This animal makes a sound."
    cat.speak(); // 输出 "Meow!"
}

trait作为参数

trait可以作为参数传递给函数,这使得函数能够接受任何实现了特定trait的类型。可以通过指定trait对象或使用泛型来实现。

//为类型实现特征  
pub trait Summary {
    fn summarize(&self) -> String;
}
pub struct Post {
    pub title: String, // 标题
    pub author: String, // 作者
    pub content: String, // 内容
}

impl Summary for Post {
    fn summarize(&self) -> String {
        format!("文章{}, 作者是{}", self.title, self.author)
    }
}

pub struct Weibo {
    pub username: String,
    pub content: String
}

impl Summary for Weibo {
    fn summarize(&self) -> String {
        format!("{}发表了微博{}", self.username, self.content)
    }
}
//使用特征作为函数参数  
//实现了Summary接口的对象  
pub fn notify(item: &impl Summary) {
    println!("Breaking news! {}", item.summarize());
}

//通过特征对泛型及参数进行约束  
//泛型必须实现了Summary特征 
pub fn notify<T: Summary>(item: &T) {
    println!("Breaking news! {}", item.summarize());
}

//多参数泛型和多函数参数 进行约束  
pub fn notify(item1: &impl Summary, item2: &impl Summary) {}
pub fn notify<T: Summary>(item1: &T, item2: &T) {}

//多重特征约束  参数需要实现多特征  
pub fn notify(item: &(impl Summary + Display)) {}
pub fn notify<T: Summary + Display>(item: &T) {}

//通过Where进行特征约束   
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
{}

//使用特征约束有条件地实现方法或特征  
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) {
        //...
    }
}

trait作为返回值

返回实现了某一特征的参数有一个限制,返回的逻辑中只能有一个具体的类型,否则会报错。

//返回实现了某一个特征的参数, 这种返回值方式有一个很大的限制,只能有一个具体的类型   
fn returns_summarizable(switch: bool) -> impl Summary {
    if switch {
        Post {
            title: String::from(
                "Penguins win the Stanley Cup Championship!",
            ),
            author: String::from("Iceburgh"),
            content: String::from(
                "The Pittsburgh Penguins once again are the best \
                 hockey team in the NHL.",
            ),
        }
    //错误 不能返回两个具体的类型  只能返回一个  
    } else {
        Weibo {
            username: String::from("horse_ebooks"),
            content: String::from(
                "of course, as you probably already know, people",
            ),
        }
    }
}

自动实现特征

在Rust中 #[derive(…)]属性用于自动实现某些特征(traits)。避免手动重复编写模板化的代码。一些常见的trait,如Clone、Copy、 Debug、 PartialEq、Eq、PartialOrd、Ord和Hash都可以自动实现。

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

常见的特征及其介绍如下表所示:

trait名称作用介绍
Clone支持通过.clone()接口创建对象的副本
Copy实现Copy的类型自动实现了Clone,于指示类型的值可以通过简单的位复制进行复制
Debug用于格式化输出,主要用于调试目的
Default用于定义类型的一个默认值Default::default()
Eq 和 PartialEq提供了部分等价的功能(即 == 和 != )
Ord 和 PartialOrd允许类型的实例进行排序
Hash从类型的实例计算一个哈希值
Iterator它允许对一系列元素进行迭代
Display用于格式化输出,主要用于用户友好的显示

#[derive]不仅限于标准库中的traits。通过使用第三方库或自定义的procedural macros,你也可以扩展#[derive]来实现自定义的traits。

关联类型

关联类型是在特征定义的语句块中,申明一个自定义类型,这样就可以在特征的方法签名中使用该类型了。这样的操作类似于给某个类型起了一个别名,在类型名称比较长的时候,可以提升开发效率。用法如下:

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> {
        // --snip--
    }
}
fn main() {
    let c = Counter{..}
    c.next()
}

默认泛型类型参数

默认类型参数主要用于两个方面:
1.减少实现的样板代码
2.扩展类型但是无需大幅修改现有的代码

//RHS的默认类型是Self  
//Self和self的区别 
//self指代的就是当前的实例对象,Self则指代的是类型名称。  
trait Add<RHS=Self> {
    type Output;
    fn add(self, rhs: RHS) -> Self::Output;
}

调用同名方法

在Rust中,如果你有多个trait同时实现了同名的方法,并且你希望在同一个类型上都实现这些trait,可以通过使用完全限定语法(Fully Qualified Syntax)来调用具体的trait方法。这种语法可以清晰地指定你想要调用的是哪个trait的哪个方法。

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*");
    }
}

fn main() {
    let person = Human;
    //调用Pilot特征上的方法
    Pilot::fly(&person); 
    //调用Wizard特征上的方法
    Wizard::fly(&person);
    //调用Human类型自身的方法
    person.fly();
}

//无self参数的函数 处理方法   
trait Animal {
    fn baby_name() -> String;
}
struct Dog;
impl Dog {
    fn baby_name() -> String {
        String::from("Spot")
    }
}
impl Animal for Dog {
    fn baby_name() -> String {
        String::from("puppy")
    }
}
//完全限定语法  
//<Type as Trait>::function(receiver_if_method, next_arg, ...);
fn main() {
    println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
}

定义特征的特征约束

可以在定义trait时指定特征约束(traits bound),这样任何实现该trait的类型都必须满足这些约束。这通常用于确保实现类型具备某些行为。这不仅提高了代码的安全性,还增强了灵活性和可重用性。

//实现Graph的具体类型都必须确保其节点和边类型实现了Clone和Debug特征
use std::fmt::Debug;
use std::clone::Clone;

trait Graph<N, E> where N: Clone + Debug, E: Clone + Debug {
    fn add_edge(&mut self, node1: N, node2: N, edge: E);
    fn add_node(&mut self, node: N);
}

//特征OutlineParint 实现了Display  
use std::fmt::Display;
trait OutlinePrint: Display {
    fn outline_print(&self) {
    }
}

在外部类型上实现外部特征

特征的孤儿原则: 如果你想要为类型A实现特征T,那么A或T至少有一个是在当前作用域中定义的。

有一个办法来绕过孤儿规则,就是把对应类型封装到一个元组结构体中,作为该结构体的一个封装字段。这样就可以在外部类型上实现外部特征了。
具体用法如下:

use std::fmt;

struct Wrapper(Vec<String>);
//通过封装的形式给类型添加特性  突破孤儿原则  
impl fmt::Display for Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}
fn main() {
    let w = Wrapper(vec![String::from("hello"), String::from("world")]);
    println!("w = {}", w);
}

精彩评论(0)

0 0 举报