收到 'Missing Lifetime specifier' 错误
Getting 'Missing Lifetime specifier' error
这是我的代码:
use std::ptr;
struct Query<T>{
data: T,
prev: & Query<T>,
next: & Query<T>,
}
impl<T> Query<T>{
fn new(name: T) -> Query<T>{
Query{
data: name,
prev: ptr::null(),
next: ptr::null(),
}
}
}
我一直在 'missing lifetime specifier' 引用 &Query<T>
。我该如何解决这个错误?
首先,请注意 &T
/&mut T
和 *const T
/*mut T
是 Rust 中不同类型的指针。前者称为 "references" 并以各种方式对其进行静态检查(包括借用分析),并且它们不能为 null 或指向某个无效对象。后者称为 "raw pointers",主要用于抽象实现和 FFI。
因此,写作
Query {
data: name,
prev: ptr::null(),
next: ptr::null(),
}
当 prev
和 next
属于 &Whatever
类型时不正确 - ptr::null()
returns 类型 *const Whatever
的值不兼容(安全地)使用 &Whatever
.
Rust 中的引用总是有一些与之关联的生命周期 - 编译器使用它来检查引用是否始终指向有效的数据片段。此生命周期在引用类型中指定:&'a T
。在大多数情况下,编译器可以自动推断出正确的生命周期,因此您很少需要将它们写在局部变量类型甚至函数声明中:
let x: &str = "hello";
fn print_str(s: &str) { println!("{}", s); }
但是,当您想要引用结构时,编译器无法在结构声明中推断生命周期(因为它可以是任意的并且取决于结构值的方式
被使用),因此您需要明确指定生命周期参数:
struct Query<'a, T> {
data: T,
prev: &'a Query<'a, T>,
next: &'a Query<'a, T>,
}
不过,在这种特殊情况下,您似乎想要实现某种双向链接结构。不幸的是,Rust 引用不能用于此。生命周期要求禁止通过引用创建循环——如果 x
中有对 y
的引用,你就不能在 y
中存储对 x
的引用。构建这种结构的唯一方法是通过突变:
let mut x = SomeStructure(None);
let y = SomeStructure(Some(&x));
x.0 = Some(&y);
但是你不能改变一个有引用指向它的对象,也就是说,上面的代码是无效的。
您要实现的目标通常被称为 intrusive data structures, and for now they are not well supported in Rust. There is an RFC issue,我们可能会在未来看到一些进展。
您可以在这里做几件事,最简单的可能是以不需要使用双向链接结构的方式重构您的代码。几乎总是可以将数据放在像 Vec
这样的连续数组中,并使用索引来访问它,而且由于缓存的局部性,即使对于简单的遍历,它也很可能会更有效率。
另一种选择是使用非侵入式链表,例如可用的链表 in the standard library。
而且,当然,您始终可以放弃使用原始指针。在需要时使用它们并没有错,大多数标准库抽象(如上面的链表)都在内部以某种方式使用它们。但是,在您的情况下,它们应该是最后的手段 - 很可能没有它们也有办法做您想做的事。
这是我的代码:
use std::ptr;
struct Query<T>{
data: T,
prev: & Query<T>,
next: & Query<T>,
}
impl<T> Query<T>{
fn new(name: T) -> Query<T>{
Query{
data: name,
prev: ptr::null(),
next: ptr::null(),
}
}
}
我一直在 'missing lifetime specifier' 引用 &Query<T>
。我该如何解决这个错误?
首先,请注意 &T
/&mut T
和 *const T
/*mut T
是 Rust 中不同类型的指针。前者称为 "references" 并以各种方式对其进行静态检查(包括借用分析),并且它们不能为 null 或指向某个无效对象。后者称为 "raw pointers",主要用于抽象实现和 FFI。
因此,写作
Query {
data: name,
prev: ptr::null(),
next: ptr::null(),
}
当 prev
和 next
属于 &Whatever
类型时不正确 - ptr::null()
returns 类型 *const Whatever
的值不兼容(安全地)使用 &Whatever
.
Rust 中的引用总是有一些与之关联的生命周期 - 编译器使用它来检查引用是否始终指向有效的数据片段。此生命周期在引用类型中指定:&'a T
。在大多数情况下,编译器可以自动推断出正确的生命周期,因此您很少需要将它们写在局部变量类型甚至函数声明中:
let x: &str = "hello";
fn print_str(s: &str) { println!("{}", s); }
但是,当您想要引用结构时,编译器无法在结构声明中推断生命周期(因为它可以是任意的并且取决于结构值的方式 被使用),因此您需要明确指定生命周期参数:
struct Query<'a, T> {
data: T,
prev: &'a Query<'a, T>,
next: &'a Query<'a, T>,
}
不过,在这种特殊情况下,您似乎想要实现某种双向链接结构。不幸的是,Rust 引用不能用于此。生命周期要求禁止通过引用创建循环——如果 x
中有对 y
的引用,你就不能在 y
中存储对 x
的引用。构建这种结构的唯一方法是通过突变:
let mut x = SomeStructure(None);
let y = SomeStructure(Some(&x));
x.0 = Some(&y);
但是你不能改变一个有引用指向它的对象,也就是说,上面的代码是无效的。
您要实现的目标通常被称为 intrusive data structures, and for now they are not well supported in Rust. There is an RFC issue,我们可能会在未来看到一些进展。
您可以在这里做几件事,最简单的可能是以不需要使用双向链接结构的方式重构您的代码。几乎总是可以将数据放在像 Vec
这样的连续数组中,并使用索引来访问它,而且由于缓存的局部性,即使对于简单的遍历,它也很可能会更有效率。
另一种选择是使用非侵入式链表,例如可用的链表 in the standard library。
而且,当然,您始终可以放弃使用原始指针。在需要时使用它们并没有错,大多数标准库抽象(如上面的链表)都在内部以某种方式使用它们。但是,在您的情况下,它们应该是最后的手段 - 很可能没有它们也有办法做您想做的事。