我正在研究《铁 rust 》中的特点和特点对象.在第Trait chapter题中,我解决第六个练习的方式与编译器的建议不同.以下代码定义了两个 struct (SheepCow)和一个特征(Animal).SheepCow都通过提供它们自己的noise方法实现来实现Animal特征.

有两个函数,random_animal_ifrandom_animal_match,它们接受参数并返回对动态特征对象的引用.

struct Sheep {}
struct Cow {}

trait Animal {
    fn noise(&self) -> String;
}

impl Animal for Sheep {
    fn noise(&self) -> String {
        "baaaaah!".to_string()
    }
}

impl Animal for Cow {
    fn noise(&self) -> String {
        "moooooo!".to_string()
    }
}

fn random_animal_if(random_number: f64) -> &'static dyn Animal {
    if random_number < 10.0 {
        &Sheep {}
    } else if random_number > 20.0 {
        &Cow {}
    } else {
        panic!()
    }
}

fn random_animal_match(random_string: &str) -> &dyn Animal {
    match random_string {
        "sheep" => &Sheep {},
        "cow" => &Cow {},
        _ => panic!(),
    }
}

fn main() {
    let animal = random_animal_if(21.0);
    println!("Randomly animal says {}", animal.noise());
    let animal = random_animal_match("sheep");
    println!("Randomly animal says {}", animal.noise());
}

这两个函数都基于输入创建并返回SheepCow对象.其中之一在浮点数输入上使用条件句.另一种方法是在给定的字符串片上使用模式匹配.逻辑是相同的,但是如果我省略了random_animal_if返回类型的&'static生存期规范,那么编译器就会抛出这个错误:

错误[E0106]:缺少生存期说明符

有趣的是,如果输入参数类型从f64更改为&str,则可以删除静态生存期注释.为什么?这两种类型有什么不同?

推荐答案

这要归功于拉斯特的lifetime elision rules分.

如果您有一个将引用作为参数并返回引用的函数,则编译器会推断输出是从输入borrow 的,如下所示:

fn random_animal_match<'a>(random_str: &'a str) -> &'a dyn Animal {

事实上,函数返回非静态引用的only种方式是从参数borrow 返回的数据.

然而,在您的代码中,random_animal_match的主体not确实borrow 了参数的返回值.编译器仍会推断出取消的生存期as if,但实际上返回类型中的生存期始终为'static.这意味着您的函数的返回类型中的生存期是过度受限的.如果函数的调用者在删除输入&str之后try 使用返回的&dyn Animal,则会得到编译器错误,即使这实际上不应该是问题:

fn main() {
    let animal = {
        let sheep = String::from("sheep");
        random_animal_match(&sheep)
    }; // - `sheep` dropped here while still borrowed

    // `sheep` does not live long enough
    println!("Randomly animal says {}", animal.noise());
}

为了最大限度地提高此函数的灵活性,应将返回类型中的生存期设置为'static:

fn random_animal_match(random_str: &str) -> &'static dyn Animal {

Rust相关问答推荐

为什么Tauri要修改被调用函数的参数名称?

如何初始化match声明中的两个变量而不会激怒borrow 判断器?

Tauri tauri—apps/plugin—store + zustand

抽象RUST中的可变/不可变引用

为潜在的下游实现使用泛型绑定而不是没有泛型绑定的trait

避免在Collect()上进行涡鱼类型的涂抹,以产生<;Vec<;_>;,_>;

如何使用RefCell::JOYMOMTborrow 对 struct 不同字段的可变引用

`Pin`有没有不涉及不安全代码的目的?

在0..1之间将U64转换为F64

将特征与具有生命周期的关联类型一起使用时的生命周期方差问题

如何在 Rust 中打印 let-else 语句中的错误?

如何保存指向持有引用数据的指针?

如何在 Rust 中显式声明 std::str::Matches<'a, P> ?

max(ctz(x), ctz(y)) 有更快的算法吗?

使用在功能标志后面导入的类型,即使未启用功能标志

一个函数调用会产生双重borrow 错误,而另一个则不会

只有一个字符被读入作为词法分析器的输入

为实现特征的所有类型实现显示

我可以在不调用 .clone() 的情况下在类型转换期间重用 struct 字段吗?

如何在 Rust 的泛型函​​数中同时使用非拥有迭代器和消费迭代器?