编者按:此代码示例来自Rust 1.0之前的版本,在语法上不是有效的Rust 1.0代码。此代码的更新版本会产生不同的错误,但答案仍然包含有价值的信息。
我在Rust 0.6:中尝试过这个代码
fn test<'r>(xs: &'r [&str]) -> &'r str {
return xs[0];
}
我认为这种类型签名的意思是:"test获取一个指向字符串的借用指针向量的带有lifetime'r的借用指针,并返回一个指向一个字符串的带有lifety'r的借来指针。但编译器说:
refs.rs:2:8: 2:12 error: mismatched types: expected `&'r str` but found `&str` (lifetime mismatch)
refs.rs:2 return xs[0];
^~~~
refs.rs:1:39: 3:1 note: the lifetime &'r as defined on the block at 1:39...
refs.rs:1 fn test<'r>(xs: &'r [&str]) -> &'r str {
refs.rs:2 return xs[0];
refs.rs:3 }
refs.rs:1:39: 3:1 note: ...does not necessarily outlive the anonymous lifetime #1 defined on the block at 1:39
refs.rs:1 fn test<'r>(xs: &'r [&str]) -> &'r str {
refs.rs:2 return xs[0];
refs.rs:3 }
error: aborting due to previous error
这似乎意味着向量中的指针可能没有(只读(向量本身那么长。这可能吗?
我需要做一些额外的注释来告诉编译器这是可以的吗?
同样,拥有指针的向量又如何呢?例如
fn test<'r>(xs: &'r [~str]) -> &'r str {
return xs[0];
}
同样,我希望能够借用一个指向向量元素的指针,至少只要我借用了整个列表。
就上下文而言,我最初的问题是试图用拥有的指针列表扩展借用点列表:
fn extend<'r>(xs: ~[&'r str], ys: &'r [~str]) -> ~[&'r str]
计划是:创建一个包含所有借用指针的exended列表,使用它,然后释放扩展列表,然后释放所拥有指针的原始列表,包括包含的字符串。
test
的第二个版本,具有所拥有/唯一的字符串,确实可以工作,只需协助编译器将~str
转换为&'r str
:
fn test<'r>(xs: &'r [~str]) -> &'r str {
let tmp: &'r str = xs[0];
tmp
}
这样做的原因是xs
向量拥有它所包含的~str
s,因此编译器知道它们的生存期至少是向量的生存期(因为它也注意在存在这样的借用时的可变性,所以字符串永远不能从向量中删除(。唯一的问题是说服编译器将xs[0]
强制为切片,这最容易由临时执行。
extend
可能看起来像:
fn extend<'r>(xs: ~[&'r str], ys: &'r [~str]) -> ~[&'r str] {
let mut xs = xs;
for vec::each(ys) |s| {
let tmp: &'r str = *s;
xs.push(tmp)
}
xs
}
vec::each(ys)
似乎有效,但ys.each
不起作用,这可能是一个错误(我现在正在调查它我打开#6655(。
如果你想修改一个矢量,通常的方法是传递一个可变的引用到矢量,即
fn extend<'r>(xs: &mut ~[&'r str], ys: &'r [~str]) {
for vec::each(ys) |s| {
let tmp: &'r str = *s;
xs.push(tmp)
}
}
其被称为类似CCD_ 10。
举例说明:
rusti> let a = &[~"a", ~"b", ~"c"];
()
rusti> test(a)
"a"
rusti> extend(~["1", "2", "3"], a)
~["1", "2", "3", "a", "b", "c"]
我认为你的意思是:
fn test<'r>(xs: &[&'r str]) -> &'r str {
return xs[0];
}
也就是说,您获取一个借用数组指针,该指针包含指向生存期为r的字符串的借用指针,并返回其中一个指针,即相同的生存期。矢量本身的寿命是无关紧要的。
这就是第二个例子不起作用的原因,因为您返回的是一个在函数输入中未借用的借用指针:数组是借用的,其内容不是。
我认为这就是你的意思:
fn get1<'r, T>(xs: &'r [T]) -> &'r T {
return &xs[0];
}
fn main() {
let a = ~[1, 2, 3];
let b = [1, 2, 3];
let c = @[1, 2, 3];
let ax = get1(a);
let bx = get1(b);
let cx = get1(c);
println(fmt!("%d %d %d", *ax, *bx, *cx));
}
特别是对于字符串,它可能不太好(因为字符串总是通过引用(,但对于值的向量,它工作得很好。
从Rust 1.19.0开始(可能从Rust 1.0开始(,原始代码按预期工作:
fn test<'r>(xs: &'r [&str]) -> &'r str {
xs[0]
}
fn main() {}
所拥有的字符串版本在更新语法后也能工作:
fn test<'r>(xs: &'r [String]) -> &'r str {
&xs[0]
}
fn main() {}
更好的是,寿命推断意味着你不需要在函数(fn test(xs: &[&str]) -> &str
,fn test(xs: &[String]) -> &str
(上有任何显式的寿命
我有一种感觉,这个问题可以归结为编译器如何计算寿命的(co-,contra-,in-(方差,或者更准确地说,在Rust 1.0之前,它是如何在这种情况下没有正确计算的。正如您正确识别的那样,因为切片包含引用,所以引用必须早于切片。因此,可以安全地返回与'r
匹配的寿命较短的字符串切片。