Rust中的所有权、引用、借用与slices

所有权
  1. Rust 中的每一个值都有一个被称为其 所有者owner)的变量。

  2. 值在任一时刻有且只有一个所有者。

  3. 当所有者(变量)离开作用域,这个值将被丢弃。

对于规则二:

// 此时"hello"的所有者为 a
let a = String::from("hello");
// move occurs 
// because `a` has type `String`, which does not implement the `Copy` trait
// 因为 String 没有实现`Copy` trait,所以"hello"的所有权转移给了 b
let b = a;
// 错误!a已经失去了所有权,不能再访问 "hello"
// println!("{}", a);

// 对于实现了`Copy` trait的类型, 如所有的基础类型(i32, u32, ... 包括 str)
// 此时"hello"的类型为: &str
let a = "hello";
let b = a;
println!("{} {}", a, b);
// 输出:hello hello

对于规则三:

{
	let a = String::from("hello");
    a;
    // a离开作用域,值被抛弃
}
// 错误!无法再次使用 a
// println!("{}", a); 

而函数体里也是一个作用域

fn main() {
    let a = String::from("hello");
    // 这里实现了所有权的交接
    let new_owner = take_ownership(a);
    // 错误!无法再次使用 a
    // println!("{}", a); 
    println!("{}", new_owner);
    // 输出:hello
}
fn take_ownership(some_str: String) -> String {
    return some_str;
    // some_str离开作用域
}
引用

引用可分为:可变引用和不可变引用

可变引用:&mut type

不可变引用:&type

其中:

& 符号就是 引用,它们允许你使用值但不获取其所有权

type 指类型名,如:i32, u32, String

一个可变变量不能同时存在多个可变引用

这个限制的好处是 Rust 可以在编译时就避免数据竞争。数据竞争data race)类似于竞态条件,它可由这三个行为造成:

  • 两个或更多指针同时访问同一数据。
  • 至少有一个指针被用来写入数据。
  • 没有同步数据访问的机制。
let mut my_str = String::from("A mutable string");
let first_mutable_reference = &mut my_str;
let second_mutable_reference = &mut my_str;
// 注意:当两个可变引用都没有被使用时,可通过编译
// 即:此时将下面的代码注释掉,可通过编译
println!("{}", first_mutable_reference);
/*
error[E0499]: cannot borrow `my_str` as mutable more than once at a time
 --> src\main.rs:5:36
  |
4 |     let first_mutable_reference = &mut my_str;
  |                                   ----------- first mutable borrow occurs here
5 |     let second_mutable_reference = &mut my_str;
  |                                    ^^^^^^^^^^^ second mutable borrow occurs here
6 |     println!("{}", first_mutable_reference);
  |                    ----------------------- first borrow later used here
*/
一个可变变量不能同时存在多个不可变引用

既然是不可变的,那么创建多少个都不会引发安全问题

let mut my_str = String::from("A mutable string");
let first_unmutable_reference = &my_str;
let second_unmutable_reference = &my_str;
println!("{} {}", first_unmutable_reference, second_unmutable_reference);
//输出:A mutable string A mutable string
一个可变变量不能同时存在可变引用和不可变引用

因为当你创建不可变引用时,你并不希望指向的量在还没有被使用时就发生改变。

let mut my_str = String::from("A mutable string");
let unmutable_reference = &my_str;
let mutable_reference = &mut my_str;
// 注意:当两个引用都没有被使用时,可通过编译
// 即:此时将下面的代码注释掉,可通过编译
println!("{}", unmutable_reference);
/*
error[E0502]: cannot borrow `my_str` as mutable because it is also borrowed as immutable
 --> src\main.rs:5:29
  |
4 |     let unmutable_reference = &my_str;
  |                               ------- immutable borrow occurs here
5 |     let mutable_reference = &mut my_str;
  |                             ^^^^^^^^^^^ mutable borrow occurs here
6 |     println!("{}", unmutable_reference);
  |                    ------------------- immutable borrow later used here
*/

而当不可变引用第一次被使用后,默认其作用域已结束,此时可以创建可变引用。

let mut my_str = String::from("A mutable string");
let unmutable_reference = &my_str;
println!("{}", unmutable_reference);
let mutable_reference = &mut my_str;
println!{"{}", mutable_reference};
// 输出:
// A mutable string
// A mutable string
借用

如在所有权中所讲的那样,当变量被传入函数体(或者说一个新的作用域)中时,变量的所用权也从原作用域转移到新的作用域中。而要在新的作用域中变量,不一定要将变量的所有权交给新的作用域,就像生活中所做的那样,我们可以将变量借出去,这就是借用

借用就是向新的作用域传递变量的引用

let let mut my_str = String::from("A mutable string");
{
    // 借用,所有权没有发生转移
    let borrow = &my_str;
    // borrow离开作用域
}
println!("{}", my_str);
// 输出:A mutable string

对于函数也是同理

fn main() {
    let a = String::from("hello");
    // 这里传入a的引用,没有交出所有权
    borrow(&a);
    println!("{}", a); 
    // 输出:
    // borrow a string: hello
    // hello
}

// 借用,所有权没有转移
fn borrow(some_str: &String) {
    println!("borrow a string: {}", some_str);
}

Rust是基于表达式的编程语言,有且仅有两种语句 (statement):

声明语句 (declaration statement),比如进行变量绑定的let语句。
表达式语句 (expression statement),它通过在末尾加上分号;来将表达式变成语句, 丢弃该表达式的值,一律返回unit()

let my_str = "Hi".to_string();
my_str; // my_str被抛弃
// 下面的代码无法通过编译
// |  println!("{}", my_str);
// |                 ^^^^^^ value borrowed here after move
// println!("{}", my_str);
slices

字符串 slicestring slice)是 String 中一部分值的引用

let s = String::from("hello world");

let hello = &s[0..5];
let world = &s[6..11];
String Slices 的结构

String Slices 的结构

fn main() {
    let mut my_str = String::from("A mutable string");
    let first_word = get_first_word(&my_str);
    // first_word 是 my_str 的部分不可变引用,
    // 而此时 first_word 尚未被使用, 所以其作用域尚未结束, 意味着 my_str 不可更改
    // 下面一行的代码无法通过编译
    // my_str.clear();
    println!("{}", first_word);
    // 输出: A
    // 而此时 my_str 可更改
    my_str.clear();
}

// 如果有一个字符串 slice,可以直接传递它, 如果有一个 String,则可以传递整个 String 的 slice
// 定义一个获取字符串 slice 而不是 String 引用的函数使得我们的 API 更加通用并且不会丢失任何功能
fn get_first_word(some_str: &str) -> &str {
    let bytes = some_str.as_bytes();
    // 因为从 .iter().enumerate() 中获取的是集合元素的引用,所以模式中使用了 &。
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' '{
            // 字符串 slice(string slice)是 String 中一部分值的 引用
            // 字符串字面值就是 slice
            // 它是一个指向二进制程序特定位置的 slice
            // 有更通用的 slice 类型
            // 如: &[i32]
            // 这里的切片与python中的切片类似
            // 只是不能有负索引
            return &some_str[..i];
        }
    }
    &some_str
}

也有更通用的 slice 类型,如:&[i32],它跟字符串 slice 的工作方式一样,通过存储第一个集合元素的引用和一个集合总长度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值