64

有时我发现自己想编写可以通过以下两种方式之一调用的函数:

// 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),但是当它不起作用时(它只允许我使用@strand ~str),我尝试了以下方法(通过复制 Rust 库),它确实有效。

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
  • 当我在做的时候,什么是'self

(我使用的是 Rust 0.7,如果它对答案有影响的话)

4

1 回答 1

69

2015-05-16 更新:原始问题中的代码适用于旧版本的 Rust,但概念保持不变。此答案已更新为使用现代 Rust 语法/库。(基本上是在最后更改~[]Vec调整~str代码String示例。)

我的理解模糊准确吗?
[...]
在上面的示例中,&str 类型的参数和 &'a str 类型的参数有什么区别?

是的,这样的一生基本上是在说“没有限制”。生命周期是将输出值与输入连接起来的一种方式,也就是说,fn foo<'a, T>(t: &'a T) -> &'a Tfoo返回一个tt作为)。这基本上意味着返回值指向所指向的内存的某个子段t

因此,类似这样的函数fn<'a>(path: &'a str) -> Vec<String>与编写非常相似{ let x = 1; return 2; }……它是一个未使用的变量。

Rust 在编写 时分配默认生命周期&str,这与编写未使用变量生命周期完全相同。ie与带有sfn(path: &str) -> Vec<String>的版本没有什么不同。'a唯一离开生命周期与包含生命周期不同的是,如果您需要强制执行全局指针(即特殊'static生命周期),或者如果您想返回一个引用(例如-> &str),这只有在返回值具有生命周期时才有可能(这必须是一个或多个输入的生命周期,或者'static)。

什么是一生?我在哪里可以了解更多关于它们的信息?

生命周期是指针指向的数据保证存在多长时间,例如,全局变量保证“永远”持续(所以它有特殊的生命周期'static)。查看它们的一种巧妙方法是:生命周期将数据连接到其所有者所在的堆栈帧;一旦该堆栈帧退出,所有者就会超出范围,并且任何指向/指向该值/数据结构的指针都不再有效,并且生命周期是编译器对此进行推理的一种方式。(使用堆栈框架视图,就好像@有一个与当前任务关联的特殊堆栈框架,并且statics 有一个“全局”堆栈框架)。

这本书还有一个生命周期章节这个要点(注意,代码现在已经过时,但概念仍然正确)是一个简洁的小演示,展示了如何使用生命周期来避免复制/分配(具有很强的安全性保证:没有悬空指针的可能性)。

当我在做的时候,什么是'self

从字面上看,没有什么特别的,只是某些地方要求类型具有生命周期(例如,在 struct/enum 定义和 s 中),并且impl目前是唯一被接受的名称。对于全局始终有效的指针,对于可以有任何生命周期的东西。这是一个错误,将(非)生命周期称为错误。'self'static'static'selfstaticself


总而言之,我会写这样的函数:

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
}
于 2013-07-05T16:46:38.607 回答