首先,请注意 &T/&mut T 和 *const T/*mut T 在 Rust 中是不同类型的指针。前者称为“引用”,以各种方式(包括借用分析)进行静态检查,不能为空或指向某个无效对象。后者称为“原始指针”,主要用于抽象实现和 FFI。
因此,写作
Query {
data: name,
prev: ptr::null(),
next: ptr::null(),
}
当 prev 和 next 的类型为 &Whatever 时不正确 - ptr::null() 返回与 &Whatever 不兼容(安全)的 *const 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,目前它们在 Rust 中没有得到很好的支持。上面有一个RFC issue,将来我们可能会看到它们的一些进展。
您可以在这里做几件事情,最简单的一件可能是重构您的代码,这样您就不需要使用双链接结构了。您几乎总是可以将数据放在一个连续的数组中,例如 Vec 并使用索引来访问它,而且由于缓存的局部性,即使是简单的遍历也很可能会更有效。
另一种选择是使用非侵入式链表,例如 in the standard library 提供的链表。
当然,您也可以随时使用原始指针。在需要时使用它们并没有错,并且大多数标准库抽象(如上面的链表)都在内部以某种方式使用它们。但是,在您的情况下,它们应该是最后的手段 - 很可能有一些方法可以在没有它们的情况下做您想做的事情。