编程语言所有权-Rust
xiu所有Rust程序都需要管理他们使用计算机内存的方式,主要用于对空闲内存的回收,在Rust编程语言在我们需要考虑代码运行在哪个上面
java的垃圾收集器会自动寻找不再使用的内存,C语言必须自己显示的分配和手动释放内存
栈内存stack
压栈出栈,存储在栈内存上的数据必须拥有固定的大小,可能发生变化的值要放在heap上,操作系统放入数据只需要存放在stack的顶端即可,不需要寻找空间,函数调用过程就相当于压栈,函数执行结束时会弹出
堆内存heap
堆内存存放数据会申请一块
区域,当存入数据时操作系统会分配这块
内存中的一部分并返回一个指针,操作系统放入数据需要记录,访问数据需要借助指针,延缓了访问速度
String
1 2 3 4 5 6 7 8
| fn main() {
let mut s = String::from("hellow");
s.push_str(" World!");
println!("{}",s); }
|

String类型存放在heap上,可以动态的修改,字符字面值char因为编译阶段被写入可行性文件里,所以不可修改
可以简单理解为在程序里定义好了一个字符字面值和一个String类型的,程序不运行
我们就可以利用winhex等软件在程序中找到字符子面值的具体内容,但是String类型不一定
drop
Rust中当变量走出了自己的作用域,drop函数会删除该内存还给操作系统
在 Rust 的所有权系统中,一个值在任何时刻只能有一个所有者。当一个值被移动到另一个变量时,所有权也随之转移,原来的变量就不能再被使用了,因为它不再拥有那个值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| fn main() {
let x=5; let y = x;
println!("x:{},y:{}",x,y);
let s1 =String::from("hello");
let s2 = s1;
println!("s1:{},s2:{}",s1,s2);
}
|

在一般语言里s1在释放的时候会销毁指向的具体数值,一旦s2销毁的时候也去销毁指向的数值就会出大问题,rust为了防止这种情况采用了移动所有权的方式,至于具体哪些类型会执行移动,哪些类型会复制取决于这个类型本身是否实现copy trait

Rust在s1移动后只会让指针本身失效,不会操作数据,这就不存在二次销毁内存的操作了

如果真的想把s1的数据复制给s2,可以使用clone方法


存在栈上面的数据不需要考虑如上问题
讨论函数赋值返回值过程中的所有权转移有助于我们理解rust安全机制
函数赋值
函数传参要么是复制要么是移动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| fn main() {
let s1 = String::from("hello");
take_owe(s1);
let n1 =1314;
take_copy(n1);
}
fn take_owe(str1:String){ println!("{}",str1); }
fn take_copy(num1:i32){ println!("{}",num1); }
|

返回值
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
| fn main() { let s1 = take_s1_have_value();
let s2 = String::from("hello");
let s3 = take_own_to_an(s2); }
fn take_s1_have_value() -> String { let str1 = String::from("hello"); str1 }
fn take_own_to_an(str1: String) -> String { str1 }
|
返回值与作用域
当一个堆数据的变量离开作用域的时候,drop函数就会清理掉这个变量的数据,除非该数据的所有权移动到另一个变量身上
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| fn main() {
let s1 = String::from("hello");
let (s2,len) = use_not_get(s1);
println!("{},{}",s2,len);
}
fn use_not_get (str1:String) -> (String,usize){ let length = str1.len(); (str1,length) }
|
引用与借用


因为s不拥有真实数据的所有权,所以即使它被销毁原来的数据也不会消失,
当被引用的变量为可变变量的时候那我们可以借助这个引用来修改原有数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| fn main() {
let mut s1 = String::from("hello");
let len = use_not_get(&mut s1);
println!("s1 is {},len is {}",s1,len);
}
fn use_not_get (str1:&mut String) -> usize{ str1.push_str(" world!"); str1.len() }
|
特定作用域内可变引用只能有一个,避免数据竞争,不可以同时存在可变引用和不可变引用,
不在一个作用域可以创建多个可变引用,在同一个作用域可以存在多个不可变引用,读写不能同时存在,可以有多个读
悬空指针
指曾经有效,现在无效的指针,Rust 的所有权和生命周期规则旨在防止悬空指针的出现。如果代码遵守 Rust 的借用规则,编译器会保证不会出现悬空指针
字符串切片是指向字符串其中一部分的引用,不获取值所有权
形式,切片值包含开始索引,不包括结束索引
[开始索引..结束索引]
[..结束索引]
[开始索引..]
1 2 3 4 5 6 7 8 9 10
| fn main() {
let s = String::from("hello world");
let hello = &s[0..6]; let world = &s[6..11];
println!("s value {}{}",hello,world);
}
|


&str
字符串字面值是切片类型

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 1. 类型和所有权: • 字符串切片 &str):是一个对字符串数据的不可变引用,它指向一段有效的 UTF-8 编码序列。&str本身不拥有数据,它只是指向数据。 • 字符串引用(&String):是对String类型的可变引用String是一个可增长的、可变的字符串类型,它拥有自己的数据。 2. 生命周期: • 字符串切片 (&str):通常与特定的生命周期(lifetime)相关联,这意味着它只能存活到其引用的数据还有效的时候。例如,如果你从一个变量中创建了一个&str,那么这个&str必须在该变量的生命周期内有效。 • 字符串引用(&String):不需要特定的生命周期,因为它是一个对String的引用,而String管理自己的生命周期。 3. 可变性: • 字符串切片(&str):总是不可变的,即使你从一个 String中创建它,你也不能通过这个切片改变String的内容。 • 字符串引用(&String):可以是可变的(&mut String),这意味着你可以通过这个引用改变String的内容。 4. 操作: • 字符串切片(&str):可以用于执行不需要修改字符串的操作,如查找子串、比较等。 • 字符串引用(&String):可以用于执行需要修改字符串的操作,如添加字符、删除字符、修改内容等。 5. 内存管理: • 字符串切片(&str):不涉及内存分配,因为它只是对现有数据的引用。 • 字符串引用(&String):涉及到内存分配,因为String可能会增长,需要动态分配内存。
|