ldd test.exe | awk '{print $3}' | grep "^/" | xargs -d "\n" cp -t .
生命周期的意义:生命周期是为了防止悬垂指针。
什么时候我们会需要用到生命周期参数标注?每个变量都有属于自己的生命周期。但是只有 当我们用到了引用,才需要考虑生命周期参数标注。每个引用都有一个生命周期参数。
A 拥有数据的所有权,那么 A 就是出借方。B 从 A 那里借用了数据,那么 B 就是借用方。 那么 A 的生命周期一定要比 B 的生命周期长。也就是说 A 要活得比 B 更长。也就是所有 权出借方的生命周期要比所有权借用方的生命周期长。为什么呢?假设 A 的生命周期比 B 的更短,那么 B 就会出现悬垂引用。
Rust 的编译器中有一个叫做生命周期检查器的工具,英文名叫做 borrow checker。在大部 分时候,它都可以自动推断出引用的生命周期参数,比如在一个函数内部。 不过在一些特殊的情况下,生命周期存在不确定的情况,这个时候就需要我们自己手动标注生命周期参数, 来告诉编译期我们希望用哪种情况,比如出现了跨函数的引用,引用用在了函数的参数、返回值的时候。
我们手动标注的生命周期参数并不会改变引用的生命周期,它只是用来帮助 borrow checker 检查我们的代码。
// 'a 是出借方
// 'b 是借用方
// 'a 的生命周期 >= 'b 的生命周期
// 'a 是 'b 的子类型,和类继承类似
fn foo<'a: 'b>() {
}
返回值引用一定和某个参数引用有关系,否则的话返回值引用的就是函数体内的局部 变量,函数结束之后就会有悬垂指针,是不允许的。例如下面的例子就无法通过编译:
fn return_str<'a>() -> &'a str {
let mut s = "Rust".to_string();
for _i in 0..3 {
s.push_str("Good ");
}
&s[..] // returns a value referencing data owned by the current function
}
fn main() {
let x = return_str();
}
再看一个例子,下面这里例子中,foo
函数的参数和返回值都标注了 'a
的生命周期,
表示返回值的生命周期至少和 x 与 y
两个生命周期中的较小者一样长,也就是
x >= 返回值 && y >= 返回值
。但是实际上返回值和参数并没有任何关系,也是通不过编
译的。
fn foo<'a>(x: &'a str, y: &'a str) -> &'a str {
let result = String::from("really long string");
result.as_str() // returns a value referencing data owned by the current function
}
fn main() {
let x = "hello";
let y = "rust";
foo(x, y);
}
满足以下 3 条规则可以自动推断生命周期:
第一条:函数的每个引用参数都有一个独立的生命周期标注。
fn foo<'a>(x: &'a i32);
fn foo<'a, 'b>(x: &'a i32, y: &'b i32);
fn foo<'a, 'b, 'c>(x: &'a i32, y: &'b i32, z: &'c i32);
第二条:如果刚好只有一个引用参数,那这个引用参数的生命周期标注直接 应用在所有返回值的引用上。
fn foo<'a>(x: &'a i32) -> &'a i32
第三条:如果方法的第一个参数是 &self
或 &mut self
,那么直接把这个参数的
生命周期标注应用在返回值的引用上。
// 应用 1 和 2 规则:
fn first_word(s: &str) -> &str
fn first_word<'a>(s: &'a str) -> &'a str
// 应用 1 规则:
fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &str
// 可以看到 2 和 3 规则推断不出来返回值的生命周期标注,所以只好手动标注。
生命周期自动推断例子:
fn print1(s: &str); // elided
fn print2(s: &'_ str); // also elided
fn print3<'a>(s: &'a str); // expanded
fn debug1(lvl: usize, s: &str); // elided
fn debug2<'a>(lvl: usize, s: &'a str); // expanded
fn substr1(s: &str, until: usize) -> &str; // elided
fn substr2<'a>(s: &'a str, until: usize) -> &'a str; // expanded
fn get_mut1(&mut self) -> &mut dyn T; // elided
fn get_mut2<'a>(&'a mut self) -> &'a mut dyn T; // expanded
fn args1<T: ToCStr>(&mut self, args: &[T]) -> &mut Command; // elided
fn args2<'a, 'b, T: ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command; // expanded
fn new1(buf: &mut [u8]) -> Thing<'_>; // elided - preferred
fn new2(buf: &mut [u8]) -> Thing; // elided
fn new3<'a>(buf: &'a mut [u8]) -> Thing<'a>; // expanded
type FunPtr1 = fn(&str) -> &str; // elided
type FunPtr2 = for<'a> fn(&'a str) -> &'a str; // expanded
type FunTrait1 = dyn Fn(&str) -> &str; // elided
type FunTrait2 = dyn for<'a> Fn(&'a str) -> &'a str; // expanded
// The following examples show situations where it is not allowed to elide the
// lifetime parameter.
// Cannot infer, because there are no parameters to infer from.
fn get_str() -> &str; // ILLEGAL
// Cannot infer, ambiguous if it is borrowed from the first or second parameter.
fn frob(s: &str, t: &str) -> &str; // ILLEGAL
/*
下面函数等价于:
fn longest<'a, 'b, 'c>(x: &'a str, y: &'b str) -> &'c str
参数 x,参数 y 和返回值都是引用,所以他们都有一个自己的生命周期参数。
但是生命周期检查器并不知道这 3 个生命周期参数之间有什么关系。
*/
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
println!("The longest string is {}", result);
}
这个例子是无法通过编译的。报错如下:
error[E0106]: missing lifetime specifier
--> src/main.rs:11:33
|
11 | fn longest(x: &str, y: &str) -> &str {
| ---- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but
the signature does not say whether it is borrowed from `x` or `y`
help: consider introducing a named lifetime parameter
|
11 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
| ^^^^ ^^^^^^^ ^^^^^^^ ^^^
我们按照上面那个例子中的提示修改代码如下:
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
这个例子可以通过编译。这个例子中的 longest
函数的生命周期参数标注中,参数 x,
参数 y 和返回值使用相同的生命周期标注'a
。表示返回值的生命周期至少和
参数 x 的生命周期和参数 y 的生命周期之间的较小者
一样长。
fn longest<'a: 'c, 'b: 'c, 'c>(x: &'a str, y: &'b str) -> &'c str {
if x.len() > y.len() {
x
} else {
y
}
}
这个例子可以通过编译。这个例子中,给每个参数和返回值都独立标注了生命周期。
'a: 'c
表示参数 x 的生命周期大于等于返回值的生命周期。
'b: 'c
表示参数 y 的生命周期大于等于返回值的生命周期。
那我们能不能给所有的参数使用一个生命周期标注,返回值单独使用一个生命周期标注呢?
fn longest<'a: 'c, 'c>(x: &'a str, y: &'a str) -> &'c str {
if x.len() > y.len() {
x
} else {
y
}
}
这个例子可以通过编译。'a: 'c
表示参数 x 的生命周期大于等于返回值的生命周期,
同时参数 y 的生命周期也大于等于返回值的生命周期
那如果返回值只和参数 x 有关系,和参数 y 没有关系,又要怎么标注?
fn longest<'a>(x: &'a str, y: &str) -> &'a str {
x
}
上面的函数等价于:
fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {
x
}
这个例子可以通过编译。可以看到返回值的生命周期参数只和参数 x 有关系。参数 x 的生命周期参数大于等于返回值的生命周期参数。
fn longest<'a: 'c, 'b, 'c>(x: &'a str, y: &'b str) -> &'c str {
x
}
这个例子可以通过编译。上面的例子也可以修改为给每个参数和返回值单独标注生命周期参 数,只要指明了返回值的生命周期标注和输入参数之间的关系就可以了。
struct A<'a> {
name: &'a str,
}
impl<'a> A<'a> {
// 等价于 fn get<'b>(&'b self) -> &'b str
// 为啥这里是 'b 而不是 'a 呢?
// 'a 是 name 引用的 s 的生命周期
// 'b 是 A 实例化出来的对象 a 的生命周期
// 根据生命周期自动推断规则,这个函数的返回值和对象 a 生命周期一样
fn get(&self) -> &str {
self.name
}
}
fn main() {
let s = String::from("hello");
let s_ref;
{
let a = A { name: &s };
s_ref = a.get();
}
println!("{:?}", s_ref);
}
这个例子是无法通过编译的。因为 s_ref
的生命周期比 a.get()
返回值的生命周期
更长。长生命周期是不能引用短生命周期的。否则就会出现悬垂指针。
error[E0597]: `a` does not live long enough
--> src/main.rs:22:13
|
22 | s_ref = a.get();
| ^ borrowed value does not live long enough
23 | }
| - `a` dropped here while still borrowed
24 |
25 | println!("{:?}", s_ref);
| ----- borrow later used here
我们之需要稍作修改,就可以通过编译:
impl<'a> A<'a> {
// 等价于 fn get<'b>(&'b self) -> &'a str
// 返回值的生命周期和 name 引用的 s 变量一样长
// 而 'b 则是代表 A 实例化出来的对象的生命周期
fn get(&self) -> &'a str {
self.name
}
}
我们把 a.get()
的返回值的生命周期不和对象 a
绑定在一起,而是和 s
绑定在一起。就能够顺利通过编译。
#[derive(Debug)]
struct ImportantExcerpt<'a> {
part: &'a str,
}
fn main() {
let noval = String::from("Call me Ishmael. Some years ago...");
let first_sentence = noval.split('.').next().expect("Could not find a '.'");
let i = ImportantExcerpt {
part: first_sentence,
};
println!("i = {:?}", i);
}
结构体 ImportantExcerpt
中的 'a
意味着 ImportantExcerpt
实例化出来的对象的
生命周期不能超过成员 part
所引用的对象的生命周期。在 main 函数中的 i
的生命
周期不能超过 noval
的生命周期。
结构体成员变量用到的生命周期标注需要在 impl 和结构体名字后面加上。
impl<'a>
是声明 'a
,而 ImportantExcerpt<'a>
是使用 'a
。
#[derive(Debug)]
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
fn level(&self) -> i32 {
3
}
fn nrp(&self, announcement: &str) -> &str {
println!("Attention please: {}", announcement);
self.part
}
}
fn main() {
let noval = String::from("Call me Ishmael. Some years ago...");
let first_sentence = noval.split('.').next().expect("Could not find a '.'");
let i = ImportantExcerpt {
part: first_sentence,
};
println!("i = {:?}", i);
}
结构体的例子比较复杂,可以对上面的例子进行如下扩展:
例子2.1:可以通过编译
impl<'a> ImportantExcerpt<'a> {
fn level(&self) -> i32 {
3
}
}
例子2.2:可以通过编译
impl<'a> ImportantExcerpt<'a> {
fn level(&'a self) -> i32 {
3
}
}
例子2.3:无法通过编译
impl<'a> ImportantExcerpt<'a> {
fn level<'a>(&'a self) -> i32 {
3
}
}
error[E0496]: lifetime name `'a` shadows a lifetime name that is already in scope
--> src/main.rs:18:12
|
8 | impl<'a> ImportantExcerpt<'a> {
| -- first declared here
...
18 | fn level<'a>(&'a self) -> i32 {
| ^^ lifetime 'a already in scope
根据报错来看,应该是 impl<'a>
声明了 'a
,然后 level<'a>
又重复声明了
'a
,在同一个作用域里声明了两次,所以报错了。
例子2.4:无法通过编译
impl<'a> ImportantExcerpt<'a> {
fn level(&'b self) -> i32 {
3
}
}
error[E0261]: use of undeclared lifetime name `'b`
--> src/main.rs:18:13
|
18 | fn level(&'b self) -> i32 {
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'b` here
|
8 | impl<'b, 'a> ImportantExcerpt<'a> {
| ^^^
help: consider introducing lifetime `'b` here
|
18 | fn level<'b>(&'b self) -> i32 {
| ^^^^
根据报错,是说 'b
没有声明,提示说可以在 impl
那里加上,也可以在函数 level
那里加上,不过没有说在 impl
那里和在函数 level
那里加上有什么区别。
例子2.5:可以通过编译
impl<'a> ImportantExcerpt<'a> {
fn level<'b>(&'b self) -> i32 {
3
}
}
例子2.6:可以通过编译
impl<'a, 'b> ImportantExcerpt<'a> {
fn level(&'b self) -> i32 {
3
}
}
例子2.7:可以通过编译
impl<'a> ImportantExcerpt<'a> {
fn nrp(&self, announcement: &str) -> &str {
println!("Attention please: {}", announcement);
self.part
}
}
例子2.8:可以通过编译
impl<'a> ImportantExcerpt<'a> {
fn nrp<'b>(&'a self, announcement: &'b str) -> &'a str {
println!("Attention please: {}", announcement);
self.part
}
}
例子2.9:可以通过编译
impl<'a> ImportantExcerpt<'a> {
fn nrp(&'a self, announcement: &'a str) -> &'a str {
println!("Attention please: {}", announcement);
self.part
}
}
例子2.10:无法通过编译
impl<'a> ImportantExcerpt<'a> {
fn nrp<'b>(&'a self, announcement: &'b str) -> &'b str {
println!("Attention please: {}", announcement);
self.part
}
}
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
--> src/main.rs:34:5
|
34 | self.part
| ^^^^^^^^^
|
note: ...the reference is valid for the lifetime `'b` as defined on the method body at 32:10...
--> src/main.rs:32:10
|
32 | fn nrp<'b>(&'a self, announcement: &'b str) -> &'b str {
| ^^
note: ...but the borrowed content is only valid for the lifetime `'a` as defined on the impl at 8:6
--> src/main.rs:8:6
|
8 | impl<'a> ImportantExcerpt<'a> {
| ^^
self.part
的生命周期是 'a
,而返回值的生命周期标注是 'b
,'a
和 'b
并没有任何关系。
例子2.11:可以通过编译
impl<'a> ImportantExcerpt<'a> {
fn nrp<'b>(&'b self, announcement: &'b str) -> &'b str {
println!("Attention please: {}", announcement);
self.part
}
}
例子2.12:可以通过编译
#[derive(Debug)]
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'b> ImportantExcerpt<'b> {
fn nrp(&'b self, announcement: &'b str) -> &'b str {
println!("Attention please: {}", announcement);
self.part
}
}
那我干脆全部改为 'b
好了,但是定义结构体的时候用的是 'a
,
这样也可以通过编译。
例子2.13:可以通过编译
impl<'a> ImportantExcerpt<'a> {
fn nrp<'b, 'c>(&'b self, announcement: &'c str) -> &'b str {
println!("Attention please: {}", announcement);
self.part
}
}
参数 announcement 和返回值没有任何关系,所以生命周期参数随便标都可以。
例子2.14:可以通过编译
impl<'a, 'b> ImportantExcerpt<'a> {
fn nrp(&'a self, announcement: &'b str) -> &'a str {
println!("Attention please: {}", announcement);
self.part
}
}
fn foo<'a, 'b>(x: &'a i32, mut y: &'b i32)
where
'a: 'b,
{
// &'a i32 is a subtype of &'b i32 because 'a: 'b
y = x;
let r: &'b &'a i32 = &&0;
}
这个例子可以通过编译。因为 'a: 'b
,所以 'a
的生命周期大于 'b
的生命周期。
短生命周期的可以引用长生命周期的。反过来就不行了。
fn foo<'a, 'b>(x: &'a i32, mut y: &'b i32)
where
'a: 'b,
{
let r: &'a &'b i32 = &&0;
}
error[E0491]: in type `&'a &'b i32`, reference has a longer lifetime than the data it references
--> src/main.rs:12:10
|
12 | let r: &'a &'b i32 = &&0;
| ^^^^^^^^^^^
因为 'a: 'b
表示 'a
的生命周期大于等于 'b
的生命周期,那么所有的生命周期标
注都满足 'static: 'x
,'static
表示静态生命周期,和整个程序的生命周期一样长。
let s: &'static str = "I have a static lifetime.";
但是要注意:
T: 'static
上面代码并不是表示 T 的生命周期和整个程序的生命周期一样长。而是说 T 只包含拥有所
有权的数据,或者是包含生命周期为 'static
的数据。T 不能包含有短生命周期的引用。
https://doc.rust-lang.org/reference/lifetime-elision.html#static-lifetime-elision
const 和 static 定义的引用默认都是 'static
的生命周期。
const 定义闭包或函数引用的时候,会应用生命周期自动推断的三条规则。
// STRING: &'static str
const STRING: &str = "bitstring";
struct BitsNStrings<'a> {
mybits: [u32; 2],
mystring: &'a str,
}
// BITS_N_STRINGS: BitsNStrings<'static>
const BITS_N_STRINGS: BitsNStrings<'_> = BitsNStrings {
mybits: [1, 2],
mystring: STRING,
};
// Resolved as `fn<'a>(&'a str) -> &'a str`.
const RESOLVED_SINGLE: fn(&str) -> &str = |x| x;
// Resolved as `Fn<'a, 'b, 'c>(&'a Foo, &'b Bar, &'c Baz) -> usize`.
const RESOLVED_MULTIPLE: &dyn Fn(&Foo, &Bar, &Baz) -> usize = &somefunc;
// There is insufficient information to bound the return reference lifetime
// relative to the argument lifetimes, so this is an error.
const RESOLVED_STATIC: &dyn Fn(&Foo, &Bar) -> &Baz = &somefunc;
// ^
// this function's return type contains a borrowed value, but the signature
// does not say whether it is borrowed from argument 1 or argument 2
fn main() {}
https://dtolnay.github.io/rust-quiz/11
fn f<'a>() {}
fn g<'a: 'a>() {}
fn main() {
let pf = f::<'static> as fn(); // late bound
let pg = g::<'static> as fn(); // early bound
println!("{}", pf == pg);
}
无法通过编译:报错如下:
error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
--> src/main.rs:18:16
|
18 | let pf = f::<'static> as fn(); // late bound
| ^^^^^^^
|
note: the late bound lifetime parameter is introduced here
--> src/main.rs:14:6
|
14 | fn f<'a>() {}
| ^^
根据报错,是说 fn f<'a>() {}
是 late bound
,所以没法直接指定为 'static
。
early bound
应该就是编译时就能够确定的。late bound
应该就是要到程序运行的时候才能够确定。struct Buffer {
buf: Vec<u8>,
pos: usize,
}
impl Buffer {
fn new() -> Buffer {
Buffer {
buf: vec![1, 2, 3, 4, 5, 6],
pos: 0,
}
}
fn read_bytes<'a>(&'a mut self) -> &'a [u8] {
self.pos += 3;
&self.buf[self.pos - 3..self.pos]
}
}
fn print(b1: &[u8], b2: &[u8]) {
println!("{:#?}, {:#?}", b1, b2);
}
fn main() {
let mut buf = Buffer::new();
let b1 = buf.read_bytes();
//let b1 = &(buf.read_bytes().to_owned());
let b2 = buf.read_bytes();
print(b1, b2);
}
报错如下,说是有两个可变借用。
error[E0499]: cannot borrow `buf` as mutable more than once at a time
--> src/main.rs:28:12
|
26 | let b1 = buf.read_bytes();
| --- first mutable borrow occurs here
27 | //let b1 = &(buf.read_bytes().to_owned());
28 | let b2 = buf.read_bytes();
| ^^^ second mutable borrow occurs here
29 | print(b1, b2);
| -- first borrow later used here
这个例子可以通过编译。
struct Buffer<'a> {
buf: &'a [u8],
pos: usize,
}
// 'a 可以认为是 main 函数中 v 的生命周期
// 'b 可以认为是 main 函数中 buf 这个对象的生命周期
// 'a 的意思是说 Buffer 这个类 new 出来的对象的生命周期不能超过 'a
impl<'a: 'b, 'b> Buffer<'a> {
fn new(b: &'a [u8]) -> Buffer {
Buffer { buf: b, pos: 0 }
}
fn read_bytes(&'b mut self) -> &'a [u8] {
self.pos += 3;
&self.buf[self.pos - 3..self.pos]
}
}
fn print(b1: &[u8], b2: &[u8]) {
println!("{:#?}, {:#?}", b1, b2);
}
fn main() {
let v = vec![1, 2, 3, 4, 5, 6];
let mut buf = Buffer::new(&v);
let b1 = buf.read_bytes();
let b2 = buf.read_bytes();
// 即使把 buf 删除了,b1 和 b2 也不受影响
drop(buf);
print(b1, b2);
}
这个例子可以通过编译。
use std::fmt::Debug;
#[derive(Debug)]
struct Ref<'a, T: 'a>(&'a T);
fn print<T>(t: T)
where
T: Debug,
{
println!("`print`: t is {:?}", t);
}
fn print_ref<'a, T>(t: &'a T)
where
T: Debug + 'a,
{
println!("`print_ref`: t is {:?}", t);
}
fn main() {
let x = 7;
let ref_x = Ref(&x);
print_ref(&ref_x);
print(ref_x);
}
https://dtolnay.github.io/rust-quiz/5
https://zhuanlan.zhihu.com/p/51616607
该例子可以通过编译。
trait Trait {
fn f(self);
}
impl<T> Trait for fn(T) {
fn f(self) {
print!("1");
}
}
impl<T> Trait for fn(&T) {
fn f(self) {
print!("2");
}
}
fn main() {
let a: fn(_) = |_: u8| {};
let b: fn(_) = |_: &u8| {};
let c: fn(&_) = |_: &u8| {};
a.f(); // 1
b.f(); // 1
c.f(); // 2
}
Trait 对象生命周期相关文档: https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes
Trait 对象默认的生命周期情况:
// For the following trait...
trait Foo { }
// These two are the same as Box<T> has no lifetime bound on T
type T1 = Box<dyn Foo>;
type T2 = Box<dyn Foo + 'static>;
// ...and so are these:
impl dyn Foo {}
impl dyn Foo + 'static {}
// ...so are these, because &'a T requires T: 'a
type T3<'a> = &'a dyn Foo;
type T4<'a> = &'a (dyn Foo + 'a);
// std::cell::Ref<'a, T> also requires T: 'a, so these are the same
type T5<'a> = std::cell::Ref<'a, dyn Foo>;
type T6<'a> = std::cell::Ref<'a, dyn Foo + 'a>;
// For the following trait...
trait Bar<'a>: 'a { }
// ...these two are the same:
type T1<'a> = Box<dyn Bar<'a>>;
type T2<'a> = Box<dyn Bar<'a> + 'a>;
// ...and so are these:
impl<'a> dyn Bar<'a> {}
impl<'a> dyn Bar<'a> + 'a {}
下面这个例子就是无法自动推断出来 Trait 对象的生命周期:
trait Foo {}
// This is an example of an error.
struct TwoBounds<'a, 'b, T: ?Sized + 'a + 'b> {
f1: &'a i32,
f2: &'b i32,
f3: T,
}
type T7<'a, 'b> = TwoBounds<'a, 'b, dyn Foo>;
// ^^^^^^^
// Error: the lifetime bound for this object type cannot be deduced from context
// 需要手动标注出来
type T7<'a, 'b, 'c> = TwoBounds<'a, 'b, dyn Foo + 'c>;
trait A {
fn value(&self) -> &str;
}
// 等价于 impl dyn A + 'static
impl dyn A {
fn trait_value(&self) -> &str {
self.value()
}
}
fn extract1(a: &dyn A) -> &str {
a.trait_value()
}
fn main() {}
这个例子是无法通过编译的。报错如下:
error[E0621]: explicit lifetime required in the type of `a`
--> src/main.rs:22:5
|
21 | fn extract1(a: &dyn A) -> &str {
| ------ help: add explicit lifetime `'static` to the type of `a`: `&'static (dyn A + 'static)`
22 | a.trait_value()
| ^^^^^^^^^^^ lifetime `'static` required
这个例子可以通过编译。
trait A {
fn value(&self) -> &str;
}
// 等价于 impl dyn A + 'static
impl dyn A {
fn trait_value(&self) -> &str {
self.value()
}
}
// 可以这么标注生命周期参数
fn extract<'t>(a: &'t (dyn A + 'static)) -> &'t str {
a.trait_value()
}
fn main() {}
这个例子可以通过编译。
trait A {
fn value(&self) -> &str;
}
impl dyn A {
fn trait_value(&self) -> &str {
self.value()
}
}
type DynA = dyn A;
fn extract3(a: &DynA) -> &str {
a.trait_value()
}
fn main() {}
这个例子可以通过编译。
trait A {
fn value(&self) -> &str;
}
impl<'a> dyn A + 'a {
fn trait_value(&self) -> &str {
self.value()
}
}
fn extract4<'t, 'm>(a: &'t (dyn A + 'm)) -> &'t str {
a.trait_value()
}
fn main() {}
use std::fmt::Debug;
trait DoSomething<T> {
fn do_sth(&self, value: T);
}
impl<'a, T: Debug> DoSomething<T> for &'a usize {
fn do_sth(&self, value: T) {
println!("{:?}", value);
}
}
fn foo<'a>(b: Box<dyn DoSomething<&'a usize>>) {
let s: usize = 10;
b.do_sth(&s);
}
fn main() {
let x = Box::new(&2usize);
foo(x);
}
这个例子是无法通过编译的。报错如下,大概意思就是说 b 的生命周期比 s
的生命周期长,函数 foo 结束的时候 s 都被销毁了,还被引用着,所以报错。
但是实际上,b.do_sth(&s)
这行代码运行结束之后,就没有用到 s 了,
所以代码逻辑上应该是没有问题的。
error[E0597]: `s` does not live long enough
--> src/main.rs:15:12
|
13 | fn foo<'a>(b: Box<dyn DoSomething<&'a usize>>) {
| -- lifetime `'a` defined here
14 | let s: usize = 10;
15 | b.do_sth(&s);
| ---------^^-
| | |
| | borrowed value does not live long enough
| argument requires that `s` is borrowed for `'a`
16 | }
| - `s` dropped here while still borrowed
解决方法见下一个例子。
修改为下面这种语法之后,就不受限制了。
fn foo(b: Box<dyn for<'f> DoSomething<&'f usize>>) {
let s: usize = 10;
b.do_sth(&s);
}
这个例子不太理解。
trait FooTrait {
fn show(&self) {}
}
impl<'a> FooTrait for &'a dyn for<'b> FooTrait
where
for<'b> dyn FooTrait: FooTrait,
{
fn show(self: &&'a dyn for<'b> FooTrait) {
println!("show 1")
}
}
impl FooTrait for for<'a> fn(&'a dyn for<'b> FooTrait) {
fn show(&self) {
println!("show 2")
}
}
fn global_test(x: &dyn for<'a> FooTrait) {
(&x).show(); // show 1
x.show(); // show 2
<&dyn for<'a> FooTrait as FooTrait>::show(&x); // show 1
}
fn main() {
let x = &(global_test as for<'a> fn(&'a dyn for<'b> FooTrait));
global_test(x);
}
这个例子可以通过编译。
trait Foo<'a> {}
struct FooImpl<'a> {
s: &'a [u32],
}
impl<'a> Foo<'a> for FooImpl<'a> {}
fn foo<'a>(s: &'a [u32]) -> Box<dyn Foo<'a> + 'a> {
Box::new(FooImpl { s: s })
}
fn main() {}
/*
pub trait Deref {
type Target: ?Sized;
pub fn deref(&self) -> &Self::Target;
}
pub trait DerefMut: Deref {
pub fn deref_mut(&mut self) -> &mut Self::Target;
}
pub trait Clone {
pub fn clone(&self) -> Self;
pub fn clone_from(&mut self, source: &Self) { ... }
}
pub trait Copy: Clone { }
&'b mut &'a mut T
-->(ok) &'b mut T
-->(no) &'a mut T
pub fn deref_mut<'b>(&'b mut self) -> &'b mut Self::Target;
pub fn deref_mut<'b>(&'b mut &'a mut T) -> &'b mut T;
&'b mut &'a T
-->(ok) &'b &'a T
-->(ok) &'b T
-->(ok) &'a T
pub fn clone<'b>(&'b self) -> Self;
pub fn clone<'b>(&'b &'a T) -> &'a T;
*/
use std::fmt::Debug;
fn foo<'a: 'b, 'b, T>(x: &'b mut &'a mut T)
where
T: Debug,
{
let a: &'b mut T = x;
println!("foo a = {:?}", a);
//let b: &'a mut T = x;
//println!("b = {:?}", b);
}
fn bar<'a: 'b, 'b, T>(x: &'b mut &'a T)
where
T: Debug,
{
let a: &'b &'a T = x;
println!("bar a = {:?}", a);
let b: &'b T = x;
println!("bar b = {:?}", b);
let c: &'a T = x;
println!("bar c = {:?}", c);
//let d: &'a &'b T = x;
//println!("bar d = {:?}", d);
}
fn main() {
{
let mut a = 10;
foo(&mut &mut a);
}
{
let mut a = 10;
bar(&mut &a);
}
}
use std::ffi::CString;
use std::os::raw::c_char;
fn main() {
let hello = String::from("hello");
let chello = CString::new(hello).unwrap();
let chello = chello.as_bytes_with_nul().as_ptr() as *const c_char;
}
use std::ffi::CString;
use std::os::raw::c_void;
fn main() {
let hello = String::from("hello");
let chello = CString::new(hello).unwrap();
let chello = chello.as_bytes_with_nul().as_ptr() as *const c_void;
}
use std::ffi::CStr;
use std::os::raw::c_char;
pub const HELLO: &'static [u8; 6usize] = b"hello\0";
fn main() {
let hello = CStr::from_bytes_with_nul(HELLO).unwrap();
let hello = hello.as_ptr() as *const c_char;
}
use std::ffi::CStr;
use std::os::raw::c_char;
fn c_to_string(hello: *const c_char) -> String {
let hello = unsafe { CStr::from_ptr(hello).to_str().unwrap() };
let hello = String::from(hello);
hello
}
fn main() {}
use std::os::raw::c_void;
use std::slice;
fn buf_to_slice(buf: *const c_void, len: usize) {
let data = unsafe { slice::from_raw_parts(buf as *const u8, len) };
// let _: () = data; // &[u8]
}
fn main() {}
项目地址:https://github.com/nanomsg/nng
先安装 ninja 依赖。
sudo apt install ninja
sudo apt install ninja-build
编译静态库:
mkdir build
cd build
cmake -G Ninja ..
ninja
编译动态库:
mkdir build
cd build
cmake -DBUILD_SHARED_LIBS=ON -G Ninja ..
ninja
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'sjtu'
# rustcc 1号源
[source.rustcc]
registry="git://crates.rustcc.com/crates.io-index"
# rustcc 2号源
[source.rustcc2]
registry="git://crates.rustcc.cn/crates.io-index"
# 清华大学
[source.tuna]
registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"
# 中国科学技术大学
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
# 上海交通大学
[source.sjtu]
registry = "https://mirrors.sjtug.sjtu.edu.cn/git/crates.io-index"
[target.arm-linux-androideabi]
linker = "/home/huangjian/local/toolchains/android_toolchains_r21b/android-ndk-r21b/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi23-clang"
[target.aarch64-linux-android]
linker = "/home/huangjian/local/toolchains/android_toolchains_r21b/android-ndk-r21b/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang"
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc-5"
先介绍一些基础背景知识。
ARM 有两层含义:
ARM CPU 架构与 x86 CPU 架构的区别:
每个 CPU 都会实现一套指令集,也叫做 Instruction Set Architecture,简称 ISA。
标题上的这些名字表示的是不同平台的 CPU 架构。
现在的手机和电脑一般都是 64 位的,所以如果要给手机编译一个软件,一般使用 aarch64 类型的交叉编译工具链。
Triple 三元组格式:
{arch}-{vendor}-{sys}-{abi}
明明是 4 个项,不知道为什么叫三元组。
例子:
arm-unknown-linux-gnueabihf
然后有的 triple 三元组会把供应商(vendor)或 ABI 给省略掉。例如:
x86_64-apple-darwin
那我们要怎么确定我们要用哪一个呢?
uname -m
查看。unknown
,Windows 是 pc
,OSX/IOS 是 apple
。uname -s
查看。ldd --version
查看。Mac 和 *BSD 忽略这个选项。Windows 是 gnu
或 msvc
。因为我们是要给 64 位的 android 系统使用,所以一定要有 aarch64 和 android 出现,这样就可以排除掉其他不可以使用的了。 下面我们看具体要怎么选。
你可以从 ndk 下载 地址下载最新的 ndk。目前最新的稳定版本是 r21b。
我这里是 ubuntu 系统,所以下载 android-ndk-r21b-linux-x86_64.zip
这个压缩包。
解压之后得到 android-ndk-r21b
这个目录,比如就解压到 HOME
目录 /home/huangjian
下面。
我们可以编写一个如下的编译脚本,来编译最简单的 hello world 的 cpp 程序。
export NDK=/home/huangjian/android-ndk-r21b
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
export TARGET=aarch64-linux-android
export API=29
export AR=$TOOLCHAIN/bin/$TARGET-ar
export AS=$TOOLCHAIN/bin/$TARGET-as
export CC=$TOOLCHAIN/bin/$TARGET$API-clang
export CXX=$TOOLCHAIN/bin/$TARGET$API-clang++
export LD=$TOOLCHAIN/bin/$TARGET-ld
export RANLIB=$TOOLCHAIN/bin/$TARGET-ranlib
export STRIP=$TOOLCHAIN/bin/$TARGET-strip
echo 'AR = '$AR
echo 'AS = '$AS
echo 'CC = '$CC
echo 'CXX = '$CXX
echo 'LD = '$LD
echo 'RANLIB = '$RANLIB
echo 'STRIP = '$STRIP
$CC hello.cpp -o hello
我们可以看到在目录 /home/huangjian/android-ndk-r21b/toolchains/llvm/prebuilt/linux-x86_64/bin
下面,有非常多的不同 CPU 架构的编译器,我们需要选择其中一个来使用。上面的编译脚本中,
我们选择的是 aarch64-linux-android29-clang
。数字 29 是 API 的级别,详见 什么是 API 级别?。
aarch64-linux-android21-clang armv7a-linux-androideabi23-clang ld.lld
aarch64-linux-android21-clang++ armv7a-linux-androideabi23-clang++ lldb-argdumper
aarch64-linux-android22-clang armv7a-linux-androideabi24-clang llvm-addr2line
aarch64-linux-android22-clang++ armv7a-linux-androideabi24-clang++ llvm-ar
aarch64-linux-android23-clang armv7a-linux-androideabi26-clang llvm-as
aarch64-linux-android23-clang++ armv7a-linux-androideabi26-clang++ llvm-cfi-verify
aarch64-linux-android24-clang armv7a-linux-androideabi27-clang llvm-config
aarch64-linux-android24-clang++ armv7a-linux-androideabi27-clang++ llvm-cov
aarch64-linux-android26-clang armv7a-linux-androideabi28-clang llvm-dis
aarch64-linux-android26-clang++ armv7a-linux-androideabi28-clang++ llvm-lib
aarch64-linux-android27-clang armv7a-linux-androideabi29-clang llvm-link
aarch64-linux-android27-clang++ armv7a-linux-androideabi29-clang++ llvm-modextract
aarch64-linux-android28-clang bisect_driver.py llvm-nm
aarch64-linux-android28-clang++ clang llvm-objcopy
aarch64-linux-android29-clang clang++ llvm-objdump
aarch64-linux-android29-clang++ clang-check llvm-profdata
aarch64-linux-android-addr2line clang-cl llvm-ranlib
aarch64-linux-android-ar clang-format llvm-readelf
aarch64-linux-android-as clang-tidy llvm-readobj
aarch64-linux-android-c++filt clang-tidy.real llvm-size
aarch64-linux-android-dwp dsymutil llvm-strings
aarch64-linux-android-elfedit git-clang-format llvm-strip
aarch64-linux-android-gprof i686-linux-android16-clang llvm-symbolizer
aarch64-linux-android-ld i686-linux-android16-clang++ sancov
aarch64-linux-android-ld.bfd i686-linux-android17-clang sanstats
aarch64-linux-android-ld.gold i686-linux-android17-clang++ scan-build
aarch64-linux-android-nm i686-linux-android18-clang scan-view
aarch64-linux-android-objcopy i686-linux-android18-clang++ x86_64-linux-android21-clang
aarch64-linux-android-objdump i686-linux-android19-clang x86_64-linux-android21-clang++
aarch64-linux-android-ranlib i686-linux-android19-clang++ x86_64-linux-android22-clang
aarch64-linux-android-readelf i686-linux-android21-clang x86_64-linux-android22-clang++
aarch64-linux-android-size i686-linux-android21-clang++ x86_64-linux-android23-clang
aarch64-linux-android-strings i686-linux-android22-clang x86_64-linux-android23-clang++
aarch64-linux-android-strip i686-linux-android22-clang++ x86_64-linux-android24-clang
arm-linux-androideabi-addr2line i686-linux-android23-clang x86_64-linux-android24-clang++
arm-linux-androideabi-ar i686-linux-android23-clang++ x86_64-linux-android26-clang
arm-linux-androideabi-as i686-linux-android24-clang x86_64-linux-android26-clang++
arm-linux-androideabi-c++filt i686-linux-android24-clang++ x86_64-linux-android27-clang
arm-linux-androideabi-dwp i686-linux-android26-clang x86_64-linux-android27-clang++
arm-linux-androideabi-elfedit i686-linux-android26-clang++ x86_64-linux-android28-clang
arm-linux-androideabi-gprof i686-linux-android27-clang x86_64-linux-android28-clang++
arm-linux-androideabi-ld i686-linux-android27-clang++ x86_64-linux-android29-clang
arm-linux-androideabi-ld.bfd i686-linux-android28-clang x86_64-linux-android29-clang++
arm-linux-androideabi-ld.gold i686-linux-android28-clang++ x86_64-linux-android-addr2line
arm-linux-androideabi-nm i686-linux-android29-clang x86_64-linux-android-ar
arm-linux-androideabi-objcopy i686-linux-android29-clang++ x86_64-linux-android-as
arm-linux-androideabi-objdump i686-linux-android-addr2line x86_64-linux-android-c++filt
arm-linux-androideabi-ranlib i686-linux-android-ar x86_64-linux-android-dwp
arm-linux-androideabi-readelf i686-linux-android-as x86_64-linux-android-elfedit
arm-linux-androideabi-size i686-linux-android-c++filt x86_64-linux-android-gprof
arm-linux-androideabi-strings i686-linux-android-dwp x86_64-linux-android-ld
arm-linux-androideabi-strip i686-linux-android-elfedit x86_64-linux-android-ld.bfd
armv7a-linux-androideabi16-clang i686-linux-android-gprof x86_64-linux-android-ld.gold
armv7a-linux-androideabi16-clang++ i686-linux-android-ld x86_64-linux-android-nm
armv7a-linux-androideabi17-clang i686-linux-android-ld.bfd x86_64-linux-android-objcopy
armv7a-linux-androideabi17-clang++ i686-linux-android-ld.gold x86_64-linux-android-objdump
armv7a-linux-androideabi18-clang i686-linux-android-nm x86_64-linux-android-ranlib
armv7a-linux-androideabi18-clang++ i686-linux-android-objcopy x86_64-linux-android-readelf
armv7a-linux-androideabi19-clang i686-linux-android-objdump x86_64-linux-android-size
armv7a-linux-androideabi19-clang++ i686-linux-android-ranlib x86_64-linux-android-strings
armv7a-linux-androideabi21-clang i686-linux-android-readelf x86_64-linux-android-strip
armv7a-linux-androideabi21-clang++ i686-linux-android-size yasm
armv7a-linux-androideabi22-clang i686-linux-android-strings
armv7a-linux-androideabi22-clang++ i686-linux-android-strip
下面列出 Rust 支持的交叉编译平台:
$ rustc --print target-list | pr -tw100 --columns 3
aarch64-fuchsia i686-pc-windows-msvc sparc-unknown-linux-gnu
aarch64-linux-android i686-unknown-cloudabi sparc64-unknown-linux-gnu
aarch64-pc-windows-msvc i686-unknown-freebsd sparc64-unknown-netbsd
aarch64-unknown-cloudabi i686-unknown-haiku sparc64-unknown-openbsd
aarch64-unknown-freebsd i686-unknown-linux-gnu sparcv9-sun-solaris
aarch64-unknown-hermit i686-unknown-linux-musl thumbv6m-none-eabi
aarch64-unknown-linux-gnu i686-unknown-netbsd thumbv7a-pc-windows-msvc
aarch64-unknown-linux-musl i686-unknown-openbsd thumbv7em-none-eabi
aarch64-unknown-netbsd i686-unknown-uefi thumbv7em-none-eabihf
aarch64-unknown-none i686-uwp-windows-gnu thumbv7m-none-eabi
aarch64-unknown-none-softfloat i686-uwp-windows-msvc thumbv7neon-linux-androideabi
aarch64-unknown-openbsd i686-wrs-vxworks thumbv7neon-unknown-linux-gnueab
aarch64-unknown-redox mips-unknown-linux-gnu thumbv7neon-unknown-linux-muslea
aarch64-uwp-windows-msvc mips-unknown-linux-musl thumbv8m.base-none-eabi
aarch64-wrs-vxworks mips-unknown-linux-uclibc thumbv8m.main-none-eabi
arm-linux-androideabi mips64-unknown-linux-gnuabi64 thumbv8m.main-none-eabihf
arm-unknown-linux-gnueabi mips64-unknown-linux-muslabi64 wasm32-unknown-emscripten
arm-unknown-linux-gnueabihf mips64el-unknown-linux-gnuabi64 wasm32-unknown-unknown
arm-unknown-linux-musleabi mips64el-unknown-linux-muslabi64 wasm32-wasi
arm-unknown-linux-musleabihf mipsel-unknown-linux-gnu x86_64-apple-darwin
armebv7r-none-eabi mipsel-unknown-linux-musl x86_64-fortanix-unknown-sgx
armebv7r-none-eabihf mipsel-unknown-linux-uclibc x86_64-fuchsia
armv4t-unknown-linux-gnueabi mipsisa32r6-unknown-linux-gnu x86_64-linux-android
armv5te-unknown-linux-gnueabi mipsisa32r6el-unknown-linux-gnu x86_64-linux-kernel
armv5te-unknown-linux-musleabi mipsisa64r6-unknown-linux-gnuabi x86_64-pc-solaris
armv6-unknown-freebsd mipsisa64r6el-unknown-linux-gnua x86_64-pc-windows-gnu
armv6-unknown-netbsd-eabihf msp430-none-elf x86_64-pc-windows-msvc
armv7-linux-androideabi nvptx64-nvidia-cuda x86_64-rumprun-netbsd
armv7-unknown-cloudabi-eabihf powerpc-unknown-linux-gnu x86_64-sun-solaris
armv7-unknown-freebsd powerpc-unknown-linux-gnuspe x86_64-unknown-cloudabi
armv7-unknown-linux-gnueabi powerpc-unknown-linux-musl x86_64-unknown-dragonfly
armv7-unknown-linux-gnueabihf powerpc-unknown-netbsd x86_64-unknown-freebsd
armv7-unknown-linux-musleabi powerpc-wrs-vxworks x86_64-unknown-haiku
armv7-unknown-linux-musleabihf powerpc-wrs-vxworks-spe x86_64-unknown-hermit
armv7-unknown-netbsd-eabihf powerpc64-unknown-freebsd x86_64-unknown-hermit-kernel
armv7-wrs-vxworks-eabihf powerpc64-unknown-linux-gnu x86_64-unknown-illumos
armv7a-none-eabi powerpc64-unknown-linux-musl x86_64-unknown-l4re-uclibc
armv7a-none-eabihf powerpc64-wrs-vxworks x86_64-unknown-linux-gnu
armv7r-none-eabi powerpc64le-unknown-linux-gnu x86_64-unknown-linux-gnux32
armv7r-none-eabihf powerpc64le-unknown-linux-musl x86_64-unknown-linux-musl
asmjs-unknown-emscripten riscv32i-unknown-none-elf x86_64-unknown-netbsd
hexagon-unknown-linux-musl riscv32imac-unknown-none-elf x86_64-unknown-openbsd
i586-pc-windows-msvc riscv32imc-unknown-none-elf x86_64-unknown-redox
i586-unknown-linux-gnu riscv64gc-unknown-linux-gnu x86_64-unknown-uefi
i586-unknown-linux-musl riscv64gc-unknown-none-elf x86_64-uwp-windows-gnu
i686-apple-darwin riscv64imac-unknown-none-elf x86_64-uwp-windows-msvc
i686-linux-android s390x-unknown-linux-gnu x86_64-wrs-vxworks
i686-pc-windows-gnu
我们可以直接搜索和 android 相关的:
$ rustc --print target-list | grep android
aarch64-linux-android
arm-linux-androideabi
armv7-linux-androideabi
i686-linux-android
thumbv7neon-linux-androideabi
x86_64-linux-android
很明显我们就是选择第一个 aarch64-linux-android
。
然后我们需要安装下这个平台的开发环境:
rustup target add aarch64-linux-android
安装好之后在 ~/.cargo/config
文件里面添加如下内容:
[target.aarch64-linux-android]
linker = "/home/huangjian/android-ndk-r21b/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang"
这里的 linker 填写的就是我们前面下载的 android 交叉编译工具目录下的编译器路径。
编译命令如下:
cargo build --target=aarch64-linux-android