Rust学习Day4: 所有权、引用和切片介绍

Rust学习Day4: 所有权、引用和切片介绍

所有权

什么是所有权: 所有权是Rust 用于如何管理内存的一组规则。所有程序都必须管理其运行时使用计算机内存的方式。一些语言中具有垃圾回收机制,在程序运行时有规律地寻找不再使用的内存;在另一些语言中,程序员必须亲自分配和释放内存。Rust 则选择了第三种方式:通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。如果违反了任何这些规则,程序都不能编译。在运行时,所有权系统的任何功能都不会减慢程序的运行

**堆:**堆是缺乏组织的:当向堆放入数据时,你要请求一定大小的空间。内存分配器(memory allocator)在堆的某处找到一块足够大的空位,把它标记为已使用,并返回一个表示该位置地址的 指针(pointer)。这个过程称作 在堆上分配内存(allocating on the heap),有时简称为 "分配"(allocating)

**栈:**栈以放入值的顺序存储值并以相反顺序取出值。这也被称作 后进先出

作用域:

说明: rust的作用域只在{}内有效,在离开作用域后,会自动调用drop函数来清理变量的堆内存

示例:离开作用域后,就会失效

rust 复制代码
fn main(){
	let s1 = String::form("hello");
	let s2 = s1; // 到这里,s1就已经释放了,因为被s2使用更改了,后面将无法使用
	//println!("{s1}, world"); // 会报错
	println!("{s2},world"); // 可以正常使用
}

赋值

说明: 当你给一个已有的变量赋一个全新的值时,也是可以的,rust会立即调用drop并释放原始值的内存

示例:

rust 复制代码
fn main(){
	let mut s = String::form("hello");
	// 重新赋值
	s = String::form("world"); // 旧的就会被丢失,然后重新赋值
	println!("{s}, world"); // 最终将会输出 world, world
}

深复制(与python的深拷贝一致)

语法: xxx.clone();

rust 复制代码
fn main(){
	let a1 = String::from("hello");
	let a2 = a1.clone(); // 克隆a1的值到a2
	println!("a1={a1}, a2={a2};"); // 输出 a1=hello, a2=hello;  说明复制成功了
}

部分类型,无需.clone() 也可

rust 复制代码
fn main(){
	let x = 5; // x是一个整数,存储在栈上
    let y = x; // y是x的一个副本,x和y都有效
    let z = x.clone(); // z也是x的一个副本,x和z都有效
    println!("x = {x}, y = {y}, z = {z}"); // 使用x和y和z  都可以正常输出
}

copy的类型:

  • 所有整数类型, 如 u32
  • 布尔类型, bool,值是true,false
  • 所有浮点类型, 比如 f64
  • 字符类型, char
  • 元组, 当且仅当其包含的类型也都实现copy的时候,比如 (i32, i32)实现了copy,但(i32, String) 就没有

所有权与函数

说明: 向函数传递值可能会被移动或者复制

示例:

rust 复制代码
fn main() {
    let s = String::from("hello");  // s 进入作用域

    takes_ownership(s);             // s 的值移动到函数里 ...
                                    // ... 所以到这里不再有效

    let x = 5;                      // x 进入作用域

    makes_copy(x);                  // x 应该移动函数里,
                                    // 但 i32 是 Copy 的,
    println!("{}", x);              // 所以在后面可继续使用 x

} // 这里,x 先移出了作用域,然后是 s。但因为 s 的值已被移走,
  // 没有特殊之处

fn takes_ownership(some_string: String) { // some_string 进入作用域
    println!("{some_string}");
} // 这里,some_string 移出作用域并调用 `drop` 方法。
  // 占用的内存被释放

fn makes_copy(some_integer: i32) { // some_integer 进入作用域
    println!("{some_integer}");
} // 这里,some_integer 移出作用域。没有特殊之处

返回值与作用域

说明: 返回值也可以被转移所有权

rust 复制代码
fn main() {
    let s1 = gives_ownership();        // gives_ownership 将它的返回值传递给 s1

    let s2 = String::from("hello");    // s2 进入作用域

    let s3 = takes_and_gives_back(s2); // s2 被传入 takes_and_gives_back, 
                                       // 它的返回值又传递给 s3
} // 此处,s3 移出作用域并被丢弃。s2 被 move,所以无事发生
  // s1 移出作用域并被丢弃

fn gives_ownership() -> String {       // gives_ownership 将会把返回值传入
                                       // 调用它的函数

    let some_string = String::from("yours"); // some_string 进入作用域

    some_string                        // 返回 some_string 并将其移至调用函数
}

// 该函数将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String {
    // a_string 进入作用域

    a_string  // 返回 a_string 并移出给调用的函数
}

引用与借用

定义: 引用(reference)像一个指针,因为它是一个地址,我们可以由此访问储存于该地址的属于其他变量的数据。与指针不同,引用在其生命周期内保证指向某个特定类型的有效值。 语法关键词&xxx

示例:

rust 复制代码
fn main(){
	let s5 = String::from("hello");
	let s5_len = calculate_length_ref(&s5);
	println!("s5 = {s5}, s5_len = {s5_len}"); // 输出s5=hello, s5_len=5;
}

fn calculate_length_ref(s: &String) -> unsize {
	s.len()  // 返回长度
}

可变引用

示例:

rust 复制代码
fn main() {
    let mut s = String::from("hello");
    change(&mut s);
    println!("s = {s}"); // 最终输出将是 hello, world
}

fn change(some_string: &mut String) {
    some_string.push_str(", world"); // 可以改变s
}

注意实现:

  1. 如下,不可同时引用
rust 复制代码
fn main(){
	let mut s = String::from("hello");

    let r1 = &mut s;
    let r2 = &mut s;

    println!("{}, {}", r1, r2);  // 会报错

}
  1. 如下,可被固定引用
rust 复制代码
fn main(){
	let mut s = String::from("hello");

	let r1 = &s;
	let r2 = &s;
	println!("r1 = {r1}, r2 = {r2}"); // 可以正确输出
	let rs = &mut s; // 也没问题,可以正常使用,不能同时出现 &mut s
}

悬垂引用

示例:悬垂引用,会被rust报错提示

rust 复制代码
fn main(){
	let reference_to_nothing = dangle(); // 试图获取一个悬吊指针,编译器会报错,因为dangle函数返回了一个指向已经离开作用域的值的引用
    println!("reference_to_nothing = {reference_to_nothing}"); // 使用reference_to_nothing
}

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

如果想要解决上面的问题,可以如下修改:

rust 复制代码
fn main(){
	let reference_to_nothing = dangle(); // 试图获取一个悬吊指针,编译器会报错,因为dangle函数返回了一个指向已经离开作用域的值的引用
    println!("reference_to_nothing = {reference_to_nothing}"); // 使用reference_to_nothing
}

fn dangle() -> String {  // 修改返回jike 
    let s = String::from("hello");
    s
}

Slice类型

定义: 切片(slice)允许你引用集合中一段连续的元素序列,而不用引用整个集合。slice 是一种引用,所以它不拥有所有权。

字符串类型:

示例:

rust 复制代码
fn main(){
	let str_char = String::from("hello world"); // str_char进入作用域

    // 字符串切换
    let hello = &str_char[0..5]; // hello是str_char的一个字符串切片,包含从索引0到索引5(不包括5)的字符
    let world = &str_char[6..11]; // world是str_char的一个
    println!("hello = {hello}, world = {world}"); // 使用hello和world

    // slice 如果是从0开始也可以不传递,如下,两个输出一样
    let slice = &str_char[0..5];
    println!("slice = {slice}"); // 使用slice
    let slice = &str_char[..5]; // 从索引0开始到索引5(不包括5)
    println!("slice = {slice}"); // 使用slice
    // 同理,如果是到字符串末尾,也可以不传递,如下,两个输出一样
    let slice = &str_char[6..11];
    println!("slice = {slice}"); // 使用slice
    let slice = &str_char[6..]; // 从索引6开始到字符串末尾
    println!("slice = {slice}"); // 使用slice
    // 同理,取完整的
    let slice = &str_char[0..11];
    println!("slice = {slice}"); // 使用slice
    let slice = &str_char[..]; // 从索引0开始到字符串末尾
    println!("slice = {slice}"); // 使用slice

    // 
    let my_string = String::from("hello world"); // my_string进入作用域
        // first_word函数返回my_string的第一个单词的切片
    let word = first_word(&my_string[0..6]);
    println!("The first word is: {word}"); // 使用word
    let word = first_word(&my_string[..]); // 直接传递整个字符串的切片
    println!("The first word is: {word}"); // 使用word
    let word = first_word(&my_string); // 直接传递整个字符串,函数会自动将其转换为切片
    println!("The first word is: {word}"); // 使用word
}

// first_word函数返回字符串的第一个单词的切片
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]; // 返回从索引0到索引i(不包括i)的字符串切片
        }
    }
    &s[..] // 如果没有找到空格,返回整个字符串的切片
}

其他类型:

示例:数字类型

rust 复制代码
fn main(){
	// 其他类型的 slice
    let a = [1, 2, 3, 4, 5];
    let slice = &a[1..3]; // slice是数组a的一个切片,包含从索引1到索引3(不包括3)的元素
    println!("slice = {:?}", slice); // 使用slice,使用{:?}来打印切
}

本人正在参考官方文档学习这门语言,如果有描述错误的,欢迎各位RUST大神指出;

相关推荐
寻寻觅觅☆5 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
l1t5 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
今天只学一颗糖5 小时前
1、《深入理解计算机系统》--计算机系统介绍
linux·笔记·学习·系统架构
赶路人儿6 小时前
Jsoniter(java版本)使用介绍
java·开发语言
testpassportcn6 小时前
AWS DOP-C02 認證完整解析|AWS DevOps Engineer Professional 考試
网络·学习·改行学it
ceclar1236 小时前
C++使用format
开发语言·c++·算法
码说AI6 小时前
python快速绘制走势图对比曲线
开发语言·python
Gofarlic_OMS7 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
星空下的月光影子7 小时前
易语言开发从入门到精通:补充篇·网络爬虫与自动化采集分析系统深度实战·HTTP/HTTPS请求·HTML/JSON解析·反爬策略·电商价格监控·新闻资讯采集
开发语言
老约家的可汗7 小时前
初识C++
开发语言·c++