Rust基础(3)-所有权、引用、slice

这些内容突出了rust对安全的关注,和别的语法差别有点大,需要多了解

所有权

  1. rust通过所有权机制来管理内存,编译器在编译就会根据所有权规则对内存的使用进行检查
  2. 堆和栈
    1. 编译时,数据的类型大小是固定的,就分配在栈上
    2. 编译时数据类型大小不固定,就分配在堆上
  3. 作用域
    1. {} 表示
  4. string内存回收
    1. string指针在栈,数据在堆
    2. 离开作用域会自动调用drop方法释放
  5. 移动
    1. 堆数据移动,会释放旧的;
    2. 栈数据移动,叫做数据拷贝,不会释放原先的
  6. clone
    1. 主要是堆数据的拷贝
  7. 栈上数据拷贝
    1. 使用copy
    2. 常用的具有copy的有:所有整型、浮点型、布尔值、字符类型、元组
  8. 函数和作用域
    1. 定义的堆变量,传入函数,相当于移动,执行完后,会被释放
    2. 定义的栈变量,传入函数,相当于拷贝,执行完后,还能继续使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
fn main() {
//3. 作用域,定义的变量只在作用域内有效,以下程序作用域外部打印y执行会失败
let x: i32 = 1;
{
let y: i32 = 1;
println!("x = {}", x);
}
// println!("y = {}", y);

//4. string内存回收
{
//定义在堆上
let mut s1 = String::from("hello");
s1.push_str(" world");
println!("s1 = {}", s1);
//离开作用域会自动调用drop方法释放
}

//5. 移动,s2拷贝s1后,s1会被释放,无法再打印
{
let s1 = String::from("hello");
println!("s1 = {}", s1);
let s2 = s1; //s1 move到s2
println!("s2 = {}", s2);
// println!("s1 = {}", s1);
}

//6. clone
{
let s1 = String::from("hello");
println!("s1 = {}", s1);
let s2 = s1.clone(); //s1 move到s2
println!("s2 = {}", s2);
println!("s1 = {}", s1);
}

//7. 栈上数据拷贝
//常用的具有copy的有:所有整型、浮点型、布尔值、字符类型、元组
let a = 1;
let b = a;
println!("a = {}, b = {}", a, b);

//8 函数和作用域
//堆的s在函数作用域被释放,不可再使用
let s = String::from("hello");
takes_ownership(s);
// println!("s = {}", s); //s不可再使用

let x = 5;
makes_copy(x);
println!("x = {}", x); //分配在栈上,拷贝,因此x可以继续使用

}

fn takes_ownership(some_string: String){
println!("{}", some_string);
}

fn makes_copy(i: i32){
println!("i = {}", i);
}

引用和借用

  1. 引用:使用符号&,类似golang 。让我们创建一个指向值的应用,但是并不拥有它,因为不拥有这个值,所以,当引用离开其值指向的作用域后也不会被丢弃
    1. &s1
  2. 借用,相当于新变量指向了旧变量;借用之后,新旧变量只能使用一个
    1. let ms = &mut s1; //借用之后,以下两行只能同时存在一行
      1. println!(“s1 = {}”, s1);
      2. println!(“ms = {}”,ms);
  3. 总结:
    1. 在任意给定时间,有了可变引用之后不能再有不可变引用
    2. 引用必须有效(如下面案例最后,方法返回了无效引用)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
fn main() {
//测试
let s1 = gives_ownership();
println!("s1 = {}", s1);
let mut s2 = String::from("hello");
let s3 = takes_and_gives_back(s2);
// println!("s2 = {}", s2); //s2被借用,已被释放,不可继续使用
println!("s3 = {}", s3);
s2 = takes_and_gives_back(s3);
println!("s2 = {}", s2);

//引用
let mut s1 = String::from("hello");
let len = calcute_leght(&s1);
println!("s1 = {}",s1);
println!("len = {}", len);

//引用不可直接修改内容,编译直接报错
//modify_s(&s1);
//如果要修改引用的内容,则需要用到借用 &mut
modify_s_mut(&mut s1);
println!("s1 = {}",s1);

let ms = &mut s1; //借用,借用后可修改,相当于 修改了s1
modify_s_mut(ms);
println!("s1 = {}", s1); //s1被借用后,s1和ms不能同时使用
// println!("ms = {}",ms); //若打印了ms,则不能再打印s1。

//方法返回被释放的变量的引用会报错
let ref_s = dangle();
}

fn gives_ownership() -> String {
let s = String::from("hello");
s
}

fn takes_and_gives_back(s: String) -> String {
s
}

//引用:读取
fn calcute_leght(s: &String) -> usize{
s.len()
}

//引用:修改,修改会失败,编译都通不过
/*fn modify_s(s:&String){
s.push_str(" world");
}*/

//引用:修改,修改会成功
fn modify_s_mut(s:&mut String){
s.push_str(" world");
}

//引用:返回被释放的变量的引用会报错(编译就会报错),堆已经被释放了
fn dangle() -> &String {
let s = String::from("hello");
&s //无效引用
}

slice

  1. 字符串slice是String中一部分值的引用
  2. 字面值就是slice
  3. 其它类型的slice
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fn main() {
let s = String::from("hello world");
let h = &s[0..5];
//let h = &s[0..=4]; //和上面同一种表示方法
//let h = &s[..5]; //和上面同一种表示方法
//let h = &s[..=4]; //和上面同一种表示方法
println!("h = {}",h);

let w = &s[6..11];
println!("w = {}",w);

let ww = &s[..];
println!("ww = {}",ww);

//字面值
let s3 = "hh";
println!("s3 = {}",s3);

let a = [1,2,3,4];
let sss = &a[1..3];
println!("sss = {}",sss[0]);
}

总结

已编辑完毕

参考

[1] Rust 程序设计语言

  • Copyrights © 2017-2023 Jason
  • Visitors: | Views:

谢谢打赏~

微信