fn main() {
let data = vec![5, 7, 3, 1];
let v = 7;
match find_pos(data, v) {
Some(pos) => println!("Found {} at {}", v, pos),
None => println!("{} Not found", v),
}
}
fn find_pos(data: Vec, v: u32) -> Option {
for (pos, item) in data.iter().enumerate() {
if *item == v {
return Some(pos);
}
}
None
}
在上面的示例代码中,main()函数定义了一个动态数组data和一个值v,然后将其传递给函数find_pos,在data中查找v是否存在,如果存在则返回v在data中的下标,如果不存在则返回None。
data 作为动态数组,因为大小在编译期无法确定,所以放在堆上,并且在栈上有一个包含了长度和容量的胖指针指向堆上的内存。
在调用find_pos函数时,main()函数中的局部变量data和v 作为参数传递给了find_pos(),因此,它们会被放在 find_pos()调用栈中的参数区:
像Java等大多数编程语言的做饭,现在堆上的内存就有了两个引用,并且每次把data作为参数传递一次,堆上的内存就会多一次引用。
那么造成的问题就是,无法得知这些引用到底能做什么操作,堆上内存什么时候能够释放,也无法确认。如果有多个调用栈引用堆上的内存时,给内存管理带来很大挑战。同时,引用是可以隐式产生的,随意性太大,例如Java中随处可见的按引用传参,它们可读可写,有很大的权限。
对于这种堆内存多次引用的问题,传统语言有如下解决方案:
以上方案,都各有利弊,都是从管理引用的角度来考虑的。
但是,此问题本质上是因为堆上的内存会被随意引用。
Rust对于值的使用,给出了以下规则:
在这三个所有权规则的约束下,上面示例的引用问题可以这样解决:
原先main()函数中的data,被移动到find_pos()后,就失效了,Rust编译器会保证main()函数随后的代码无法访问这个变量,就确保了堆上的内存依旧只有唯一的引用。解决了堆上数据的多重引用。
main()函数中在把data传递给find_pos()后,还想让main()函数能够访问的化,可以调用clone()方法,把data复制一份出来。
但是这种手动复制,会让代码变得复杂,一些只存储在栈上的简单类型数据,如果要避免所有权转移之后不能访问的情况,只能频繁的手动clone复制。
Rust给出了两种方案:
符合Copy语义的类型,在赋值或传参时,值会自动按位拷贝。
如果数据类型没有实现Copy trait,在赋值或者函数调用的时候无法Copy,那么就按默认的Move语义。
也就是说,要移动一个值,如果值的类型实现了Copy trait,就会自动使用Copy语义进行拷贝,否则使用Move语义进行移动。
但是Copy trait也有一定的限制,Copy trait和Drop trait 不能共存。一旦你实现了Copy trait,就无法实现Drop trait。反之亦然。
fn is_copy() {}
fn types_impl_copy_trait() {
is_copy::();
is_copy::();
// 所有的整数类型都是 copy
is_copy::();
is_copy::();
is_copy::();
is_copy::();
is_copy::();
is_copy::();
is_copy::();
is_copy::();
is_copy::();
is_copy::();
is_copy::();
// 函数指针是copy
is_copy::();
// 裸指针是 copy
is_copy::<*const String>();
is_copy::<*mut String>();
// 不可变引用是 copy
is_copy::<&String>();
is_copy::<&[Vec]>();
//对于数组/元组,如果其内部类型是 copy,那么它们就是 copy
is_copy::<[u8; 4]>();
is_copy::<(&str, &str)>();
}
fn types_not_impl_copy_trait() {
// DST 类型不是 copy
// is_copy::();
// is_copy::<[u8]>();
// 有堆内存的类型不是 copy
//is_copy::>();
//is_copy::();
//可变引用不是 copy
//is_copy::<&mut String>();
// 对于数组/元组,如果其内部类型不是 copy,那么它们也不是copy
//is_copy::<[Vec;4]>;
//is_copy::<(String,u23)>();
}
Borrow语义通过引用语法(&或者&mut)来实现。
在Rust中,“借用”和“引用”是一个概念。所有的引用都只是借用了“临时使用权”,它并不破坏值的单一所有权约束。
在默认的情况下,Rust的借用都是只读的。
本质上,引用是一个受控的指针,指向某个特定的类型。
其他传统语言,函数传参有两种方式:传值(pass-by-value)和 传引用(pass-by-reference)。
比如在Java,给函数传一个整数,这是传值,与Rust中的Copy语义一致。
给函数传一个对象,或者是任何堆上的数据结构,Java会自动隐式地传引用。
但是Java的引用是对象的别名,这也导致随着程序的运行,同一块内存的引用到处都是,不得不依赖GC进行内存回收。
Rust中没有传引用的概念,Rust所有的参数传递都是传值,不管是Copy还是Move。
所以,在Rust中,必须显式地把某个数据的引用,传给另一个函数。
Rust的引用实现了Copy trait,所以按照Copy语义,此引用会被复制一份交给要调用的函数。
对这个函数来说,它并不拥有数据本身,数据只是临时借给它使用。所有权还在原来的拥有者那里。
在Rust里,引用是一等公民,和其他数据类型地位相等。
fn main() {
let data = vec![1, 2, 3, 4]; // data 的生命周期比sum()中对data的引用要长
let data1 = &data; // 借用不能超过值的生存期
// 值的地址是什么?引用的地址又是什么?
println!(
"address of value: {:p}({:p}),address of data: {:p},data1: {:p}", //0x8aa19bf8a8(0x8aa19bf8a8),address of data: 0x8aa19bf948,data1: 0x8aa19bf8c0
&data, //0x8aa19bf8a8
data1, //0x8aa19bf8a8
&&data, //0x8aa19bf948
&data1 //0x8aa19bf8c0
);
println!("sum of data1: {}", sum(&data)); // sum 函数处在 main() 函数下一层调用栈中,它结束后main函数还会继续执行
// 堆上数据的地址是什么
println!(
"address of items:[{:p},{:p},{:p},{:p}]", //address of items:[0x1cc59705670,0x1cc59705674,0x1cc59705678,0x1cc5970567c]
&data[0], //0x1cc59705670
&data[1], //0x1cc59705674
&data[2], //0x1cc59705678
&data[3], //0x1cc5970567c
);
}
fn sum(data: &[u32]) -> u32 {
//值的地址会改变吗,引用的地址会改变吗
println!(
"address of value: {:p},addr of ref: {:p}", //address of value: 0x1cc59705670,addr of ref: 0x8aa19bf6d0
data, //0x1cc59705670
&data //0x8aa19bf6d0
);
data.iter().sum()
}
// 只读引用 实现了 Copy trait,也就意味着引用的赋值、传参都会产生新的浅拷贝
// 虽然data 有很多只读引用指向它,但是堆上的数据依旧只有data 一个所有者:值的任意多个引用并不影响所有权的唯一性。
data1、&data和传到sum()里的data1' 都是指向data本身,这个值的地址是固定的。但是它们引用的地址都是不同的。这是因为 只读引用实现了Copy trait,也就是意味着引用的赋值、传参都会产生新的浅拷贝。堆上数据依旧只有data一个所有者。值的任意多个引用并不影响所有权的唯一性。
借用(引用)不能超过(overlive)值的生存期。
堆变量的生命周期不具备任意长短的灵活性,因为堆上内存的生死存亡,跟栈上的所有者牢牢绑定。而栈上内存的生命周期,又跟栈的生命周期相关,所以,我们只需要关心调用栈的生命周期。
为了保证内存安全,Rust对可变引用的使用做了严格的约束:
页面更新:2024-05-02
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号