在 self 上指定生命周期时,Rust 在 trait 上使用动态多态性的问题
Issue with Rust using dynamic polymorphism on trait when specifying lifetime on self
我终于决定尝试一下 Rust(1.7 和 1.8)。来自 C++,我必须说 Rust 看起来很棒。我试图在 C++ 中重现一个众所周知的行为,该行为包括对一组对象使用动态多态性。
我遇到了一个我能够解决的非常烦人的问题,但我想知道你们对这个问题的看法,我希望它可以帮助其他正在尝试做同样问题的人
让我们考虑以下实现初始想法的代码:
struct Foo
{
foo: u32,
}
trait Bar
{
fn bar(& self) -> u32;
}
impl Bar for Foo
{
fn bar(& self) -> u32
{
return self.foo;
}
}
fn foobar(l: &std::collections::LinkedList<& Bar>)
{
for i in l
{
println!("{}", i.bar());
}
}
fn main()
{
let foo0 = Foo{foo: 8u32};
let foo1 = Foo{foo: 8u32};
let mut l = std::collections::LinkedList::new();
l . push_back(&foo0 as &Bar);
l . push_back(&foo1 as &Bar);
foobar(&l);
}
这里一切都在编译,一切都在完美地工作。
我想传递另一个对特征 Bar
的函数 bar
的引用,结果我不得不为 Bar
特征添加生命周期。为了简单起见,我将只添加生命周期(因为它将在 Rust 1.8 下很好地编译)。通过整个代码添加生命周期后,代码最终看起来像这样:
struct Foo
{
foo: u32,
}
trait Bar<'a>
{
fn bar(& 'a self) -> u32;
}
impl<'a> Bar<'a> for Foo
{
fn bar(& 'a self) -> u32
{
return self.foo;
}
}
fn foobar<'a>(l: &std::collections::LinkedList<& 'a Bar>)
{
for i in l
{
println!("{}", i.bar());
}
}
fn main()
{
let foo0 = Foo{foo: 8u32};
let foo1 = Foo{foo: 8u32};
let mut l = std::collections::LinkedList::new();
l . push_back(&foo0 as &Bar);
l . push_back(&foo1 as &Bar);
foobar(&l);
}
如果编译这段代码,错误信息如下:
../test.rs:21:12: 21:13 error: cannot infer an appropriate lifetime due to conflicting requirements [E0495]
../test.rs:21 for i in l
^
../test.rs:19:1: 25:2 help: consider using an explicit lifetime parameter as shown: fn foobar<'a>(l: &std::collections::LinkedList<&'a Bar>)
../test.rs:19 fn foobar<'a>(l: &std::collections::LinkedList<& 'a Bar>)
../test.rs:20 {
../test.rs:21 for i in l
../test.rs:22 {
../test.rs:23 println!("{}", i.bar());
../test.rs:24 }
...
error: aborting due to previous error
到这里问题就很明显了,编译器知道push_back
中给出的变量有不同的生命周期,因此它不能符合和不同意我写的。如果在同一个let
语句中声明变量foo0
和foo1
,问题就可以解决。
我发现很难找出该代码中的错误所在。
我的问题是:
有没有办法告诉编译器一个集合可能需要不同的生命周期?
通过在另一个参考变量(这里没有出现)而不是 self
上设置 'a
生命周期,代码可以编译。这是否意味着如果我们不指定任何生命周期,那么编译器会放置与我在上一个问题中询问的特定生命周期相对应的 '_
?
是否有一个隐含的规则"forbids"我们为自己设置生命周期?
那段代码是 Rust 惯用的吗?
如果将 foobar
的定义更改为:
,第二个代码示例将编译通过
fn foobar<'a>(l: &std::collections::LinkedList<&'a Bar<'a>>) {
for i in l {
println!("{}", i.bar());
}
}
但是,你对生命周期参数所做的事情对我来说意义不大。
当我们想要一个return引用的方法时,我们通常会定义一个在整个生命周期内通用的特征(例如您的 Bar
的第二个版本)实施者的成员。例如,假设我们有以下结构:
struct Foo<'a> {
foo: &'a str,
}
此结构包含一个引用,我们必须明确命名该生命周期。我们希望允许任何生命周期,所以我们定义了一个生命周期参数,'a
.
我们可以给这个结构添加一个固有的方法:
impl<'a> Foo<'a> {
fn foo(&self) -> &'a str {
self.foo
}
}
请注意 &self
没有明确的生命周期。相反,我们在 foo
方法的 return 类型上使用 impl
中的 'a
参数,因为我们希望 returned 值的生命周期为与结构中的生命周期相同,而不是 self
的生命周期(通常会更短)。
如果我们想类似地定义一个特征方法怎么办?
trait Bar<'a> {
fn bar(&self) -> &'a str;
}
impl<'a> Bar<'a> for Foo<'a> {
fn bar(&self) -> &'a str {
self.foo
}
}
特征 Bar
定义了生命周期参数,impl
将其链接到 Foo
的生命周期参数。
除了在特征上添加生命周期参数,您还可以在各个方法上添加生命周期参数。
例如,考虑这个特征:
trait Slice {
fn slice<'a>(&self, s: &'a str) -> &'a str;
}
我们希望结果与 s
参数具有相同的生命周期。为此,我们需要在方法上定义一个生命周期参数并将其应用于两个引用。
为了完整起见,这里是该特征的一个实现:
struct SliceBounds {
start: usize,
end: usize,
}
impl Slice for SliceBounds {
fn slice<'a>(&self, s: &'a str) -> &'a str {
&s[self.start..self.end]
}
}
我终于决定尝试一下 Rust(1.7 和 1.8)。来自 C++,我必须说 Rust 看起来很棒。我试图在 C++ 中重现一个众所周知的行为,该行为包括对一组对象使用动态多态性。
我遇到了一个我能够解决的非常烦人的问题,但我想知道你们对这个问题的看法,我希望它可以帮助其他正在尝试做同样问题的人
让我们考虑以下实现初始想法的代码:
struct Foo
{
foo: u32,
}
trait Bar
{
fn bar(& self) -> u32;
}
impl Bar for Foo
{
fn bar(& self) -> u32
{
return self.foo;
}
}
fn foobar(l: &std::collections::LinkedList<& Bar>)
{
for i in l
{
println!("{}", i.bar());
}
}
fn main()
{
let foo0 = Foo{foo: 8u32};
let foo1 = Foo{foo: 8u32};
let mut l = std::collections::LinkedList::new();
l . push_back(&foo0 as &Bar);
l . push_back(&foo1 as &Bar);
foobar(&l);
}
这里一切都在编译,一切都在完美地工作。
我想传递另一个对特征 Bar
的函数 bar
的引用,结果我不得不为 Bar
特征添加生命周期。为了简单起见,我将只添加生命周期(因为它将在 Rust 1.8 下很好地编译)。通过整个代码添加生命周期后,代码最终看起来像这样:
struct Foo
{
foo: u32,
}
trait Bar<'a>
{
fn bar(& 'a self) -> u32;
}
impl<'a> Bar<'a> for Foo
{
fn bar(& 'a self) -> u32
{
return self.foo;
}
}
fn foobar<'a>(l: &std::collections::LinkedList<& 'a Bar>)
{
for i in l
{
println!("{}", i.bar());
}
}
fn main()
{
let foo0 = Foo{foo: 8u32};
let foo1 = Foo{foo: 8u32};
let mut l = std::collections::LinkedList::new();
l . push_back(&foo0 as &Bar);
l . push_back(&foo1 as &Bar);
foobar(&l);
}
如果编译这段代码,错误信息如下:
../test.rs:21:12: 21:13 error: cannot infer an appropriate lifetime due to conflicting requirements [E0495]
../test.rs:21 for i in l
^
../test.rs:19:1: 25:2 help: consider using an explicit lifetime parameter as shown: fn foobar<'a>(l: &std::collections::LinkedList<&'a Bar>)
../test.rs:19 fn foobar<'a>(l: &std::collections::LinkedList<& 'a Bar>)
../test.rs:20 {
../test.rs:21 for i in l
../test.rs:22 {
../test.rs:23 println!("{}", i.bar());
../test.rs:24 }
...
error: aborting due to previous error
到这里问题就很明显了,编译器知道push_back
中给出的变量有不同的生命周期,因此它不能符合和不同意我写的。如果在同一个let
语句中声明变量foo0
和foo1
,问题就可以解决。
我发现很难找出该代码中的错误所在。 我的问题是:
有没有办法告诉编译器一个集合可能需要不同的生命周期?
通过在另一个参考变量(这里没有出现)而不是
self
上设置'a
生命周期,代码可以编译。这是否意味着如果我们不指定任何生命周期,那么编译器会放置与我在上一个问题中询问的特定生命周期相对应的'_
?是否有一个隐含的规则"forbids"我们为自己设置生命周期?
那段代码是 Rust 惯用的吗?
如果将 foobar
的定义更改为:
fn foobar<'a>(l: &std::collections::LinkedList<&'a Bar<'a>>) {
for i in l {
println!("{}", i.bar());
}
}
但是,你对生命周期参数所做的事情对我来说意义不大。
当我们想要一个return引用的方法时,我们通常会定义一个在整个生命周期内通用的特征(例如您的 Bar
的第二个版本)实施者的成员。例如,假设我们有以下结构:
struct Foo<'a> {
foo: &'a str,
}
此结构包含一个引用,我们必须明确命名该生命周期。我们希望允许任何生命周期,所以我们定义了一个生命周期参数,'a
.
我们可以给这个结构添加一个固有的方法:
impl<'a> Foo<'a> {
fn foo(&self) -> &'a str {
self.foo
}
}
请注意 &self
没有明确的生命周期。相反,我们在 foo
方法的 return 类型上使用 impl
中的 'a
参数,因为我们希望 returned 值的生命周期为与结构中的生命周期相同,而不是 self
的生命周期(通常会更短)。
如果我们想类似地定义一个特征方法怎么办?
trait Bar<'a> {
fn bar(&self) -> &'a str;
}
impl<'a> Bar<'a> for Foo<'a> {
fn bar(&self) -> &'a str {
self.foo
}
}
特征 Bar
定义了生命周期参数,impl
将其链接到 Foo
的生命周期参数。
除了在特征上添加生命周期参数,您还可以在各个方法上添加生命周期参数。
例如,考虑这个特征:
trait Slice {
fn slice<'a>(&self, s: &'a str) -> &'a str;
}
我们希望结果与 s
参数具有相同的生命周期。为此,我们需要在方法上定义一个生命周期参数并将其应用于两个引用。
为了完整起见,这里是该特征的一个实现:
struct SliceBounds {
start: usize,
end: usize,
}
impl Slice for SliceBounds {
fn slice<'a>(&self, s: &'a str) -> &'a str {
&s[self.start..self.end]
}
}