我想通过返回
impl trait
来隐藏从 create 函数返回的实际实现,如 create_trait()
中所示。这怎么办?
trait Names<'a> {
fn names(&'a self) -> &Vec<&'a str>;
}
struct NamesImpl<'b> {
names: Vec<&'b str>,
}
impl<'c> Names<'c> for NamesImpl<'c> {
fn names(&'c self) -> &Vec<&'c str> {
&self.names
}
}
fn create_impl<'i>() -> NamesImpl<'i> {
NamesImpl {
names: vec!["Hello", "world"],
}
}
#[allow(dead_code)]
fn create_trait<'t>() -> impl Names<'t> {
NamesImpl {
names: vec!["Hello", "world"],
}
}
fn main() {
let names_impl = create_impl();
println!("Names: {:?}", names_impl.names());
//This doesn't compile, see error below
let names_trait = create_trait();
println!("Names: {:?}", names_trait.names());
}
我无法理解以下编译错误。为什么它可以与
create_impl()
一起使用,但不能与 create_trait()
一起使用?
error[E0597]: `names_trait` does not live long enough
--> src/main.rs:34:29
|
34 | println!("Names: {:?}", names_trait.names());
| ^^^^^^^^^^^ borrowed value does not live long enough
35 | }
| -
| |
| `names_trait` dropped here while still borrowed
| borrow might be used here, when `names_trait` is dropped and runs the destructor for type `impl Names<'_>`
在 Rust 中使用生命周期的一般经验法则是,如果您不知道自己在做什么,请让编译器为您推断正确的生命周期。一旦您获得更多经验,您就会了解何时“实际上”需要使用显式生命周期注释,但您的示例不是这些情况之一。我可以通过删除所有不必要的生命周期注释来编译它:
trait Names {
fn names(&self) -> &Vec<&str>;
}
struct NamesImpl<'a> {
names: Vec<&'a str>,
}
impl Names for NamesImpl<'_> {
fn names(&self) -> &Vec<&str> {
&self.names
}
}
fn create_impl() -> NamesImpl<'static> {
NamesImpl { names: vec!["Hello", "world"] }
}
fn create_trait() -> impl Names {
NamesImpl { names: vec!["Hello", "world"] }
}
fn main() {
let names_impl = create_impl();
println!("Names: {:?}", names_impl.names());
// compiles
let names_trait = create_trait();
println!("Names: {:?}", names_trait.names());
}
,然后仅将生命周期注释添加回编译器特别要求我添加的区域。
(谢谢椒盐锤!)中的这一部分让我大开眼界。它让我看到在这个建筑中
trait Names<'a> {
fn names(&'a self) -> &Vec<&'a str>;
}
调用
names()
将在其剩余生命周期中借用该结构。
使用显式生命周期注释在原始代码中纠正此问题是可能的,但结果远非完美。我只是尝试这样做,试图更好地理解“为什么”。trait Names<'a : 'f, 'f> {
fn names(&self) -> &Vec<&'f str>;
}
struct NamesImpl<'b> {
names: Vec<&'b str>,
}
impl <'c : 'f, 'f> Names<'c, 'f> for NamesImpl<'c> {
fn names(&self) -> &Vec<&'f str> {
&self.names
}
}
fn create_impl<'i : 'f, 'f>() -> NamesImpl<'i> {
NamesImpl{names: vec!["Hello", "world"]}
}
#[allow(dead_code)]
fn create_trait<'t : 'f, 'f>() -> impl Names<'t, 'f> {
NamesImpl{names: vec!["Hello", "world"]}
}
fn main() {
let names_impl = create_impl();
println!("Names: {:?}", names_impl.names());
//This compiles
let names_trait = create_trait();
println!("Names: {:?}", names_trait.names());
}
结论:遵循Shepmaster的建议
impl Names<'t>
)时,这个问题与滴胶问题有关,并且 Rust 编译器必须决定在给定签名的情况下使用生命周期参数实际是什么。
这是 GAT 的另一个类似问题,在通用测试函数的上下文中它也是不透明类型。 (方差问题第 1 部分 - 在 Rust 中创建 Wayland 合成器工具箱的冒险,以及精确解释 NLL 与 dropglue?特征上的生命周期参数 - 帮助 - Rust 编程语言论坛) 还要注意以下事实的细微差别:
Trait<'a>
是生命周期不变的
'a
,但结构是协变的。具体针对操作员的问题,似乎虽然是完全不透明的类型,但返回的 't
的生命周期参数必须比返回值的整个存在时间更长,直到它被删除。然而,
impl Names<'t>
方法声称在同一段时间内借用 self,但我们无法提供。names
我自己仍然对这个问题感到怀疑,因为直到我搜索并进入这个主题之前我还没有找到任何相关文档。 (试图理解类似的 GAT 滴胶问题)。如果我有任何遗漏或错误,欢迎更正和提供材料。
但有一点很清楚,他们是完全正确的,只需去掉
//This doesn't compile, see error below
let names_trait = create_trait();
println!("Names: {:?}", names_trait.names());
--> projects/temp/src/main.rs:34:29
|
33 | let names_trait = create_trait();
| ----------- binding `names_trait` declared here
34 | println!("Names: {:?}", names_trait.names());
| ^^^^^^^^^^^ borrowed value does not live long enough
...
37 | }
| -
| | // EDITED: drop(names_trait)
| `names_trait` dropped here while still borrowed
| // EDITED: <---- 't ended here, for which in names_trait.names() the names_trait should be borrowed
| borrow might be used here, when `names_trait` is dropped and runs the destructor for type `impl Names<'_>`
那个无用的生命周期参数,因为
trait Names<'a>
只自己参与
'a
方法的契约,将其与实现者的生命周期参数绑定是多余的或特质的生命周期参数。或者至少,如果你想保留它,就拥有它。names