一些方法也会造成panic。例如,Option类型可以包含某些东西或者什么也不包含。如果在它上面执行unwrap()操作,当它什么也不包含的时候,就会panic(译者注:这里panic感觉保留原词比较好,翻译成恐慌感觉怪怪的,打架了理解意思就好)。
fn main() {
let o1: Option = Some(128);
o1.unwrap(); // this is fine
let o2: Option = None;
o2.unwrap(); // this panics!
}
// output: thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/libcore/option.rs:378:21
复制代码
Option 不是一个结构体,它是一个带有两个变量的enum
enum Option {
None,
Some(T),
}
impl Option {
fn unwrap(self) -> T {
// enums variants can be used in patterns:
match self {
Self::Some(t) => t,
Self::None => panic!(".unwrap() called on a None option"),
}
}
}
use self::Option::{None, Some};
fn main() {
let o1: Option = Some(128);
o1.unwrap(); // this is fine
let o2: Option = None;
o2.unwrap(); // this panics!
}
// output: thread 'main' panicked at '.unwrap() called on a None option', src/main.rs:11:27
复制代码
Result也是一个枚举(enum),它可以包含某物或者是一个错误:
enum Result {
Ok(T),
Err(E),
}
复制代码
如果包含的是一个错误,那么它在进行unwrap操作的时候也会panic。
变量绑定有一个"生命周期(lifetime)":
fn main() {
// `x` doesn't exist yet
{
let x = 42; // `x` starts existing
println!("x = {}", x);
// `x` stops existing
}
// `x` no longer exists
}复制代码
类似地,引用也有一个生命周期:
fn main() {
// `x` doesn't exist yet
{
let x = 42; // `x` starts existing
let x_ref = &x; // `x_ref` starts existing - it borrows `x`
println!("x_ref = {}", x_ref);
// `x_ref` stops existing
// `x` stops existing
}
// `x` no longer exists
}复制代码
引用的生命周期不能超过它所借用(引用)的变量绑定的声明周期:
fn main() {
let x_ref = {
let x = 42;
&x
};
println!("x_ref = {}", x_ref);
// error: `x` does not live long enough
}
复制代码
一个变量绑定可以进行多次不可变借用:
fn main() {
let x = 42;
let x_ref1 = &x;
let x_ref2 = &x;
let x_ref3 = &x;
println!("{} {} {}", x_ref1, x_ref2, x_ref3);
}
复制代码
当变量绑定被借用的时候,是不能被修改的:
fn main() {
let mut x = 42;
let x_ref = &x;
x = 13;
println!("x_ref = {}", x_ref);
// error: cannot assign to `x` because it is borrowed
}
复制代码
当一个变量(绑定)被不可变借用(引用)的时候,就不能再被可变借用(引用):
fn main() {
let mut x = 42;
let x_ref1 = &x;
let x_ref2 = &mut x;
// error: cannot borrow `x` as mutable because it is also borrowed as immutable
println!("x_ref1 = {}", x_ref1);
}
复制代码
函数参数里的引用也有生命周期:
fn print(x: &i32) {
// `x` is borrowed (from the outside) for the
// entire time this function is called.
}
复制代码
带有引用参数的函数可以被拥有不同生命周期的借用(借用)来调用,所以:
所有带有引用参数的函数都是泛型的
生命周期是泛型参数
生命周期的命名以'开头:
// elided (non-named) lifetimes:
fn print(x: &i32) {}
// named lifetimes:
fn print(x: &'a i32) {}
复制代码
这就允许函数返回引用,返回的引用的生命周期依赖于参数的生命周期:
struct Number {
value: i32,
}
fn number_value(num: &'a Number) -> &'a i32 {
&num.value
}
fn main() {
let n = Number { value: 47 };
let v = number_value(&n);
// `v` borrows `n` (immutably), thus: `v` cannot outlive `n`.
// While `v` exists, `n` cannot be mutably borrowed, mutated, moved, etc.
}复制代码
当只有单个输入生命周期的时候,可以不进行命名(译者注:就就是可以省略不写),函数里所有的一切都有相同的生命周期,所以下面的两个函数是等价的:
fn number_value(num: &'a Number) -> &'a i32 {
&num.value
}
fn number_value(num: &Number) -> &i32 {
&num.value
}复制代码
结构体关于生命周期也可以是泛型的,这使得结构体里可以含有引用:
struct NumRef {
x: &'a i32,
}
fn main() {
let x: i32 = 99;
let x_ref = NumRef { x: &x };
// `x_ref` cannot outlive `x`, etc.
}
复制代码
下面是相同的代码,但是带有一个额外的函数:
struct NumRef {
x: &'a i32,
}
fn as_num_ref(x: &'a i32) -> NumRef {
NumRef { x: &x }
}
fn main() {
let x: i32 = 99;
let x_ref = as_num_ref(&x);
// `x_ref` cannot outlive `x`, etc.
}复制代码
下面也是相同的代码,但是带有"省略(elided)"的生命周期:
struct NumRef {
x: &'a i32,
}
fn as_num_ref(x: &i32) -> NumRef {
NumRef { x: &x }
}
fn main() {
let x: i32 = 99;
let x_ref = as_num_ref(&x);
// `x_ref` cannot outlive `x`, etc.
}复制代码
impl块在生命周期方面也是泛型的:
impl NumRef {
fn as_i32_ref(&'a self) -> &'a i32 {
self.x
}
}
fn main() {
let x: i32 = 99;
let x_num_ref = NumRef { x: &x };
let x_i32_ref = x_num_ref.as_i32_ref();
// neither ref cannot outlive `x`
}
复制代码
但是你也可以在这里进行省略:
impl NumRef {
fn as_i32_ref(&self) -> &i32 {
self.x
}
}
复制代码
如果你都不需要生命周期的命名,还可以省略得更多一些:
impl NumRef {
fn as_i32_ref(&self) -> &i32 {
self.x
}
}复制代码
有一个特殊的生命周期叫'static',它的意思是在整个程序的运行期间都是有效的。
字符串字面量(String literals)就是'static':
struct Person {
name: &'static str,
}
fn main() {
let p = Person {
name: "fasterthanlime",
};
}复制代码
但是有所有权的字符串不是静态(全局变量)的:
struct Person {
name: &'static str,
}
fn main() {
let name = format!("fasterthan{}", "lime");
let p = Person { name: &name };
// error: `name` does not live long enough
}复制代码
在上面的例子里,局部的name不是一个'static str',它是一个String。它是被动态分配然后将会被释放。它的生命周期要比整个程序的生命周期短(尽管它在main函数里)。
为了在Person里存储一个非-'static'的字符串,可以采取下面两种方式:
A)写成关于生命周期的泛型:
struct Person {
name: &'a str,
}
fn main() {
let name = format!("fasterthan{}", "lime");
let p = Person { name: &name };
// `p` cannot outlive `name`
}
复制代码
或者
B)拥有字符串的所有权:
struct Person {
name: String,
}
fn main() {
let name = format!("fasterthan{}", "lime");
let p = Person { name: name };
// `name` was moved into `p`, their lifetimes are no longer tied.
}
复制代码
顺便提一下:在一个结构体字面量里,当一个字段被赋值为一个具有相同名字的变量绑定时:
let p = Person { name: name };
复制代码
它可以被简写成下面这样:
let p = Person { name };
复制代码
对于Rust中的许多类型,都有被拥有(owned)的和不被拥有(non-owned)的变量:
字符串(Strings): String是被拥有(owned)的,&str是一个引用(reference)。
路径(Paths):PathBuf是被拥有(owned)的, &Path是一个引用(reference);
集合(Collections): Vec是被拥有(owned)的,&[T]是一个引用(reference)。
欢迎关注我的微信公众号: