08.Rust引用和借用
Rust引用
- 接续上一章,Rust中使用元组进行返回string时,是使用了string本身,但是使用对象的引用作为参数能够更好执行。
fn main() {let s1 = String::from("Hello!");let len = calculate_length(&s1);println!("The length of {} is {}",s1,len);
}fn calculate_length(s: &String) -> usize
{s.len()
}
这个代码中就使用了引用,
下面是官方文档
- 这里的s中指针指向s1指针,通过s1指针再指向数据索引,完成引用,但是并不会拥有这个值,在引用停止时值也不会被抛弃
- 相对应的有解引用,之后会介绍
- 引用标识符是&
fn main() {let s1 = String::from("hello");let len = calculate_length(&s1);println!("The length of '{}' is {}.", s1, len);
}fn calculate_length(s: &String) -> usize { // s 是对 String 的引用s.len()
} // 这里,s 离开了作用域。但因为它并不拥有引用值的所有权,// 所以什么也不会发生
- 因为没有所有权,所以在引用结束之后并不丢弃它指向的数据。
将创建一个引用的行称为借用,引用默认状态下不允许修改被引用的变量值
fn main() {let s = String::from("hello");change(&s);
}fn change(some_string: &String) {some_string.push_str(", world");
}$ cargo runCompiling ownership v0.1.0 (file:///projects/ownership)
error[E0596]: cannot borrow `*some_string` as mutable, as it is behind a `&` reference--> src/main.rs:8:5|
7 | fn change(some_string: &String) {| ------- help: consider changing this to be a mutable reference: `&mut String`
8 | some_string.push_str(", world");| ^^^^^^^^^^^ `some_string` is a `&` reference, so the data it refers to cannot be borrowed as mutableFor more information about this error, try `rustc --explain E0596`.
error: could not compile `ownership` due to previous error
可变引用
- 在上段代码基础上添加mut将其变为可变引用。
- 可变引用在同一时间下,只能有一个对应一个特殊的可变引用,尝试使用两个将会失败。
fn main() {let mut s = String::from("hello");change(&mut s);
}fn change(some_string: &mut String) {some_string.push_str(", world");
}
fn main() {let mut s = String::from("hello");let r1 = &mut s;let r2 = &mut s;println!("{}, {}", r1, r2);
}
$ cargo runCompiling ownership v0.1.0 (file:///projects/ownership)
error[E0499]: cannot borrow `s` as mutable more than once at a time--> src/main.rs:5:14|
4 | let r1 = &mut s;| ------ first mutable borrow occurs here
5 | let r2 = &mut s;| ^^^^^^ second mutable borrow occurs here
6 |
7 | println!("{}, {}", r1, r2);| -- first borrow later used hereFor more information about this error, try `rustc --explain E0499`.
error: could not compile `ownership` due to previous error
- 这样限制的好处是Rust在编译过程中避免数据竞争。
数据竞争产生条件
- 两个或更多指针同时访问同一数据。
- 至少有一个指针被用来写入数据。
- 没有同步数据访问的机制。
避免数据竞争最好的方式是使用不同作用域。
fn main() {let mut s = String::from("hello");{let r1 = &mut s;} // r1 在这里离开了作用域,所以我们完全可以创建一个新的引用let r2 = &mut s;
}
- 同时也不能在使用不可变引用的同时使用可变引用,但是可以同时使用多个不可变引用,因为只能读取的变量不会相互影响。
fn main() {let mut s = String::from("hello");let r1 = &s; // 没问题let r2 = &s; // 没问题let r3 = &mut s; // 大问题println!("{}, {}, and {}", r1, r2, r3);
}
$ cargo runCompiling ownership v0.1.0 (file:///projects/ownership)
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable--> src/main.rs:6:14|
4 | let r1 = &s; // no problem| -- immutable borrow occurs here
5 | let r2 = &s; // no problem
6 | let r3 = &mut s; // BIG PROBLEM| ^^^^^^ mutable borrow occurs here
7 |
8 | println!("{}, {}, and {}", r1, r2, r3);| -- immutable borrow later used hereFor more information about this error, try `rustc --explain E0502`.
error: could not compile `ownership` due to previous error
- 可以在不可变引用作用域结束的时候使用可变引用。
fn main() {let mut s = String::from("hello");let r1 = &s; // 没问题let r2 = &s; // 没问题println!("{} and {}", r1, r2);// 此位置之后 r1 和 r2 不再使用let r3 = &mut s; // 没问题println!("{}", r3);
}
悬垂引用
- 其他具有指针的语言中很容易通过释放内存时保留指向它的指针错误生成悬垂指针,这个指针中指向的内存已经不是之前的内存。
- Rust中则保证了数据不会在引用之前离开作用域。
fn main() {let reference_to_nothing = dangle();
}fn dangle() -> &String {let s = String::from("hello");&s
}$ cargo runCompiling ownership v0.1.0 (file:///projects/ownership)
error[E0106]: missing lifetime specifier--> src/main.rs:5:16|
5 | fn dangle() -> &String {| ^ expected named lifetime parameter|= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime|
5 | fn dangle() -> &'static String {| ^^^^^^^^For more information about this error, try `rustc --explain E0106`.
error: could not compile `ownership` due to previous error
- 在这段代码中由于没有生命周期,所以没法引用,因为这个引用会造成悬垂指针。
- 但是如果在函数中使用返回String:
fn main() {let string = no_dangle();
}fn no_dangle() -> String {let s = String::from("hello");s
}
- 将s所有权移出,值不被释放,此时不会有错误。
切片slice类型
- slice数据类型也没有所有权,slice允许使用集合中一段连续元素序列,不需用引用所有集合。
- 下面是一段官方文档:
- 需要逐个检查
String
中值是否为空格,使用as_bytes
将String
转换为字节数组:
let bytes = s.as_bytes();
- 使用
iter
在字节数组上创建迭代器,iter()返回集合中每个元素,enumerate
包装iter
结果, - enumerate中第一个元素是索引,第二个元素是集合中元素引用。返回的结果是
(i,&item)
。 - 通过字节的字面量语法寻找代表空格的字节,找到空格返回空格位置,负责返回s.len()。
- 返回了独立的
usize
数字,但是无法保证是否有效。
fn first_word(s: &String) -> usize {let bytes = s.as_bytes();for (i, &item) in bytes.iter().enumerate() {//找到空格,b' '表示空格字节if item == b' ' {return i;}}s.len()
}fn main() {let mut s = String::from("hello world");let word = first_word(&s); // word 的值为 5s.clear(); // 这清空了字符串,使其等于 ""// word 在此处的值仍然是 5,// 但是没有更多的字符串让我们可以有效地应用数值 5。word 的值现在完全无效!
}
- word此时值仍然为5,但是很难将word中索引和s相对应。在将5保存到word中时s中的内容改变(这里可能是地址)
字符串slice
- 字符串slice类似这样,对String中一部分值的引用。
fn main() {let s = String::from("hello world");let hello = &s[0..5];let world = &s[6..11];
}
接下来是一段官方文档的描述:
- Rust中range语法引用和slice相同。
fn main() {let s = String::from("hello");let slice = &s[0..2];
let slice = &s[..2];
}
- 以下语法也是相同的
fn main() {let s = String::from("hello");let len = s.len();let slice = &s[3..len];
let slice = &s[3..];
}
也可以舍弃[start..end]
中的start
和end
fn main() {let s = String::from("hello");let len = s.len();let slice = &s[0..len];
let slice = &s[..];
}
- 字符串slice的声明写做
&str
fn first_word(s: &String) -> &str {let bytes = s.as_bytes();for (i, &item) in bytes.iter().enumerate() {if item == b' ' {return &s[0..i];}}&s[..]
}fn main() {let mut s = String::from("hello world");let word = first_word(&s);s.clear(); // error!println!("the first word is: {}", word);}
这时候如果直接编译会出现错误。因为s.clear()会清除String,Rust不允许word中不可变引用和clear中可变引用同时存在,导致编译失败。
修改过后的代码
- 取消清除
s.clear()
字符串字面量就是slice
let s = "hello world!";
- 这里的s类型是
&str
,是一个指向二进制程序特定位置的slice,字符串字面变量是不可变的,&str
是一个不可变引用
fn first_word(s: &String) -> &str
//下面代码可以对String和&str使用相同函数。
fn first_word(s: &str) -> &str
- 修改过后,会使得我们的API更加通用,不会丢失内容。
fn first_word(s: &str) -> &str {let bytes = s.as_bytes();for (i, &item) in bytes.iter().enumerate() {if item == b' ' {return &s[0..i];}}&s[..]
}fn main() {let my_string = String::from("hello world");// `first_word` 接受 `String` 的切片,无论是部分还是全部let word = first_word(&my_string[0..6]);let word = first_word(&my_string[..]);// `first_word` 也接受 `String` 的引用,// 这等同于 `String` 的全部切片let word = first_word(&my_string);let my_string_literal = "hello world";// `first_word` 接受字符串字面量的切片,无论是部分还是全部let word = first_word(&my_string_literal[0..6]);let word = first_word(&my_string_literal[..]);// 因为字符串字面值**就是**字符串 slice,// 这样写也可以,即不使用 slice 语法!let word = first_word(my_string_literal);
}
其它类型的slice
let a = [1,2,3,4,5];
let a = [1,2,3,4,5];
let slice = &a[1..3];
- 这种slice类型是
&[i32]
,和字符串slice工作方式相同。
08.Rust引用和借用相关推荐
- rust 案例_理解Rust的引用与借用
困惑 接触Rust也一段时间了,但对References(引用) .borrowing(借用) . &关键字.ref关键字.* 关键字,这几个词非常困惑.常见的问题不在乎下面几条: & ...
- rust: 引用第三方库(Cargo.toml、Cargo.lock文件)
接下来我要使用随机数生成函数,这个函数在 rand 库中.我们在 Cargo.toml 文件中的依赖项中,加入对 rand 的依赖说明. [package] name = "game&quo ...
- rust编程-rust所有权理解(chapter 4.2 引用实质是借用)
目录 2. 引用与借用 2.1 可变(mutable)引用 2.2 悬空(dangling)引用 2.3 引用的规则总结 2. 引用与借用 上一章节中提到,所有权在函数调用中的转移,函数返回必须同时返 ...
- Programming Rust Fast, Safe Systems Development(译) 引用(第五章 完)
Libraries cannot provide new inabilities. -Mark Miller 我们已经看到的所有指针类型 - 简单的Box 堆指针,以及String和Vec值内部的指针 ...
- rust的所有权与引用
所有权 所有权是rust最独特的特性,它让rust无需GC就可以保证内存安全. 什么事所有权 rust的核心特性就是所有权 所有程序在运行时都必须管理他们使用计算机内存的方式 有些语言有垃圾收集机制, ...
- Rust学习:5_所有权与借用
Rust学习:5_所有权与借用 前言 为了学习Rust,阅读了github上的Rust By Practice电子书,本文章只是用来记录自己的学习过程,感兴趣的可以阅读原书,希望大家都能掌握Rust! ...
- RUST语言的编程范式
总是有很多很多人来问我对Rust语言怎么看的问题,在各种地方被at,其实,我不是很想表达我的想法.因为在不同的角度,你会看到不同的东西.编程语言这个东西,老实说很难评价,在学术上来说,Lisp就是很好 ...
- 年前最后一项技能树 Rust ,攻碉堡 ing (Bilibili 视频整理)
前言 我自己的语言语言学习树 最新的新闻 最近的新闻,Rust 审核团队,集体辞职抗议, 还是linux 社区的 linus 牛皮,镇得住场子不要慌,咱们是在linux 社区混的,影响不大,该学还是得 ...
- rust高级矿场_高级 Rust 所有权管理
暨 Repository 简介 引子Rust 在处理数据时表现第一好情况的是处理树状数据,第二好的情况是数据能够看作有向无环图,但是其中执行数据变化的修改脉络路径仍然能看作是树形,同样好的也还有粗粒度 ...
最新文章
- SAP QM 执行事务代码QA11 报错- Selected set code does not exist, or data entered is incomplete-
- SpringBoot 2.0 系列001 -- 入门介绍以及相关概念
- 【网络】高性能网络编程--下一个10年,是时候考虑C10M并发问题了
- 深入Java中的位操作
- Linux学习笔记033_10
- put the eye care sticker on the neck is relief
- VTK:PolyData之ColorCells
- bct对oracle影响,Oracle 优化篇+Rman增量备份优化之BCT(块变更追踪)
- java自定义对话框_Flutter AlertDialog自定义对话框实现示例(确认或取消)
- 谷歌浏览器之如何调试页面js
- 做CV和做NLP,是否都有光明的未来?
- 浅析ReentrantReadWriteLock读写锁
- 小小数据统计(柱状图、折线图、扇形图)
- 直接学python3_新手应该学python2还是python3?
- python协程详解_彻底搞懂python协程-第一篇(关键词1-4)
- C# 网上收集的一些所谓的开源项目
- 虚拟桌面和云桌面办公系统
- 相机投影变换(位姿)
- 小白到学会python要多久_零基础小白多久能学会python
- STM32不能进入睡眠模式