偶尔我发现自己想要编写可以通过以下两种方式之一调用的函数:
// With a string literal:
let lines = read_file_lines("data.txt");
// With a string pointer:
let file_name = ~"data.txt";
let lines = read_file_lines(file_name);
我的第一个猜测是使用借用的指针(&str
)作为参数类型,但是当它没有用时(它只允许我使用@str
和{{1} }),我尝试了以下(通过复制Rust库),这确实有效。
~str
问题是我不明白我在做什么。从我可以收集的内容(主要来自编译器错误),我宣布一个没有限制的生命周期,并用它来描述路径参数(意味着任何生命周期都可以作为参数传递)。
所以:
fn read_file_lines<'a>(path: &'a str) -> ~[~str] {
let read_result = file_reader(~Path(path));
match read_result {
Ok(file) => file.read_lines(),
Err(e) => fail!(fmt!("Error reading file: %?", e))
}
}
类型参数与&str
类型参数有什么区别?&'a str
是什么?(我使用Rust 0.7,如果它对答案产生影响)
答案 0 :(得分:56)
更新2015-05-16 :原始问题中的代码应用于旧版本的Rust,但概念保持不变。此答案已更新为使用现代Rust语法/库。 (基本上将~[]
更改为Vec
,将~str
更改为String
并在结尾处调整代码示例。)
我的理解是否含糊不清? [...]
类型&amp; str的参数与上面示例中&amp;'a str的参数之间有什么区别?
是的,像这样的一生都说基本上是“没有限制”。生命周期是一种将输出值与输入连接起来的方法,即fn foo<'a, T>(t: &'a T) -> &'a T
表示foo
返回一个与t
具有相同生命周期的指针,也就是说,它指向的数据对于与t
相同的时间长度(严格地说,至少与此一样长)。这基本上意味着返回值指向t
指向的内存的某个子部分。
因此,像fn<'a>(path: &'a str) -> Vec<String>
这样的函数与编写{ let x = 1; return 2; }
非常相似......它是一个未使用的变量。
Rust在编写&str
时分配默认生命周期,这完全等同于编写未使用的变量生命周期。即fn(path: &str) -> Vec<String>
与'a
s的版本没有区别。离开生命周期的唯一时间不同于包括它是否需要强制执行全局指针(即特殊'static
生命周期),或者如果要返回引用(例如-> &str
)只有在返回值具有生命周期时才可能(并且这必须是一个或多个输入的生命周期,或'static
)。
一生如何?我在哪里可以了解更多关于它们的信息?
生命周期是指针指向的数据保证存在多长时间,例如全局变量保证持续“永久”(因此它具有特殊的生命期'static
)。查看它们的一种巧妙方法是:生命周期将数据连接到其所有者所在的堆栈框架;一旦该堆栈帧退出,所有者就会超出范围,并且任何指向/进入该值/数据结构的指针都不再有效,并且生命周期是编译器对此进行推理的一种方式。 (使用堆栈帧视图,就好像@
具有与当前任务关联的特殊堆栈帧,static
具有“全局”堆栈帧。
还有一个lifetimes chapter of the book和this gist(注意代码现已过时,但概念仍然是真的)是一个很好的小演示,说明如何使用生命周期来避免复制/分配(具有强大的安全保证:没有悬挂指针的可能性)。
虽然我在这,但
'self
是什么?
字面上没什么特别的,只是某些地方需要类型才能有生命周期(例如在结构/枚举定义和impl
s中),目前'self
和'static
是唯一可接受的名称。 'static
用于全局始终有效的指针,'self
用于可以具有任何生命周期的内容。调用(非static
)生命周期除self
以外的任何内容都是错误。
总而言之,我会写下这样的功能:
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::Path;
fn read_file_lines(path: &Path) -> Vec<String> {
match File::open(path) {
Ok(file) => {
let read = BufReader::new(file);
read.lines().map(|x| x.unwrap()).collect()
}
Err(e) => panic!("Error reading file: {}", e)
}
}
fn main() {
let lines = read_file_lines(Path::new("foo/bar.txt"));
// do things with lines
}