Rust 编程学习笔记Day 5 - Borrow
昨天(day4) 我们一起学习了Copy语义,在进行变量赋值,传参,函数返回时,如果变量的数据结构实现了Copy trait,就自动使用Copy语义,否则就使用Move语义 转移所有权,后面无法访问该变量。
还有一些数据结构既没有实现Copy trait,也不想转移所有权。这时候就需要用到今天的主角--Borrow语义。
Borrow语义
从名字就不难看出,变量的所有权并不会发生转移,就可以被其他上下文借用了。 就像小时借同桌的橡皮, 长大了借房东的房子(要付钱)。 过年回家借女友,来应对亲戚催婚的问题。 很明显借来的,并没有所有权,只拥有临时使用权。 Borrow 语义通过引用语法(& 或者 &mut)来实现。
其实,在 Rust 中,“借用”和“引用”是一个概念,只不过在其他语言中引用的意义和 Rust 不同,所以 Rust 提出了新概念“借用”,便于区分。(你到是便于区分了,对于我们这些新手有点制造概念,增加学习成本了。我按照你的套路理解一大圈,然后你告诉我借用就是引用,就这?)。
默认情况下,Rust的借用都是只读的,就好像借来的房子,退租的时候,里面不能少东西,也不能把人家原来的装修风格变了。当然借来的女友,也不能被写入任何数据。
所以,如果我们想避免 Copy 或者 Move,可以使用借用,或者说引用。
只读借用/只读引用
我们在学习其他语言的时候,函数传参一般方式有:传值,传引用。
但在Rust中没有传引用的概念,Rust所有参数传递都是传值,包括Copy和Move。 在Rust中,必须显式地把某个数据的引用,传给另一个函数。(我理解:传值就是copy栈上的值,不论栈上存的是“指针/地址”还是“值”。)
Rust的引用实现了Copy trait(疑问:只读借用,可变借用都实现了吗?) 所以按照 Copy 语义,这个引用会被复制一份交给要调用的函数。对这个函数来说,它并不拥有数据本身,数据只是临时借给它使用,所有权还在原来的拥有者那里。
在 Rust 里,引用是一等公民,和其他数据类型地位相等。
用之前的错误代码来演示
fn main() {
let data = vec![1, 2, 3, 4];
let data1 = data;
println!("sum of data1: {}", sum(data1));
println!("data1: {:?}", data1); // error1
println!("sum of data: {}", sum(data)); // error2
}
fn sum(data: Vec<u32>) -> u32 {
data.iter().fold(0, |acc, x| acc + x)
}
通过调整成借用的方式,通过编译,并查看值和引用的地址。
fn main() {
let data = vec![1, 2, 3, 4];
let data1 = &data;
// 值的地址是什么?引用的地址又是什么?
println!(
"addr of value: {:p}({:p}), addr of data {:p}, data1: {:p}",
&data, data1, &&data, &data1
);
println!("sum of data1: {}", sum(data1));
// 堆上数据的地址是什么?
println!(
"addr of items: [{:p}, {:p}, {:p}, {:p}]",
&data[0], &data[1], &data[2], &data[3]
);
}
fn sum(data: &Vec<u32>) -> u32 {
// 值的地址会改变么?引用的地址会改变么?
println!("addr of value: {:p}, addr of ref: {:p}", data, &data);
data.iter().fold(0, |acc, x| acc + x)
}
先别着急run,我们先想一下,data值的地址是否不变,而data1引用的地址,在传给sum()函数后,是否指向同一个地址。 3,2,1.
我们来验证一下,心里的答案 正确与否。
可以看到data1,&data,以及sum()里的data1'都指向data的ptr。这个值是不变的。 但是每个引用的地址都不一样,这是因为上面说到的只读借用实现了Copy trait。 也就意味着引用的赋值(data1 = &data) 引用的传参(sum(data1)) 都会产生新的浅拷贝。
现在我们看到虽然有多个只读引用指向了data,但在堆上的真实数据而言,它只属于data一个人,所以值的多个引用,并不影响所有权的唯一性。
那么问题来了!一旦data离开了作用域,被释放了。还有指向data的引用,那就会变成我们想避免的悬挂指针了?类似Golang里的逃逸?
我们明天接着聊!我是老张一个陪你成长的码农。
相关文章
- Rust语言尝鲜
- rust学习笔记:for循环的一些问题
- rust语言流程控制
- Rust编程学习笔记Day6 Borrow的生命周期及约束规则
- Rust变成学习笔记Day8 值在哪里创建?
- Rust变成学习笔记Day9 值的使用及如何销毁?
- Rust学习笔记Day13 怎么用trait实现子类型多态?
- Rust学习笔记Day14 常用trait之内存篇
- Rust学习笔记Day21 为什么Rust的错误处理与众不同?
- rust 入门笔记:环境安装、hello World、Cargo
- Rust学习笔记之结构体
- Rust学习笔记之错误处理
- Rust编程实现Redis的强大功能(用rust开发redis)
- Rust构建Redis连接应用(rust 连接redis)