研读Rust圣经解析——Rust learn-6(引用与借用,Slice)

引用与借用

在上一篇文章中我们说了所有权转移-作用域消费的问题,我们应该清楚这样的问题很平常,十分常见,而我们更希望我们能够在变量传入作用域之后依然能够使用,甚至是使用在作用域中经过一些处理之后的变量,因此引用产生

在书中很清楚的将引用与借用分开,认为:
引用:引用(reference)像一个指针,因为它是一个地址,我们可以由此访问储存于该地址的属于其他变量的数据。 与指针不同,引用确保指向某个特定类型的有效值。
借用:我们将创建一个引用的行为称为 借用(borrowing)

然而在我看来他们并不用区分,因为我认为他们都是获取一个变量的值而不抢占其所有权的行为,所以在我的文章里不将他们分的很清

如下是一个通过引用a的值,scope_area借用后返回的过程:

fn scope_area(b: &mut String) -> &String {
    
    
    b.push_str("hello");
    return b;
}


fn main() {
    
    
    let mut a = String::from("test");
    scope_area(&mut a);
    println!("{}", a);
}

我们一样来理解一下(这里我就不分开了),scope_area借用了a的值,进行一系列的处理,最后将a的值的处理结果返回,还给了变量a

好比是:一个人租用了你的车,出去玩好之后,把车做了个保养最后把车还给你了,车还是你的车,只是车经过了一些”修饰“

引用紊乱(同时使用可变和不可变引用)

这个问题是我们在使用不可变引用的同时也使用了可变引用产生的,当然换一下顺序依然!

如下:

fn main() {
    
    
    let mut s = String::from("hello");
    let r1 = &s; // 没问题
    let r2 = &s; // 没问题
    let r3 = &mut s; // 有问题
    println!("{}, {}, and {}", r1, r2, r3);
}

我们来理解一下:r1和r2都是不变的,我们将其看成只读,当两个人在看一个非常严谨的文章时,r3这个可变的去把文章修改了,这就出现了问题,不可变的引用和可变引用若同时进行处理,那我们应该以哪一个为准呢?是改过的再去工作,还是没改过的?

处理

如何处理引用紊乱呢?我们想到如果引用被消费掉了就没有问题了,没错在合适的时候消费引用即可避免这种问题

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);
}

或者我们也可以采取不同作用域来区分开来

fn main() {
    
    
    let mut s = String::from("hello");

    {
    
    
        let r1 = &s; // 没问题
        let r2 = &s; // 没问题
    }

    let r3 = &mut s; // 没问题
    println!("{}", r3);
}

悬垂引用

在具有指针的语言中,很容易通过释放内存时保留指向它的指针而错误地生成一个 悬垂指针(dangling pointer),所谓悬垂指针是其指向的内存可能已经被分配给其它持有者。
我们看一下如下的列子:

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

fn main() {
    
    
    let a = dangle();
}

在这里例子中,在函数中声明了一个变量s,然后将s的引用返回回去,在main中a获取了这个函数的返回值,那么难道真的能获取到吗?实际上a无法获取到返回值
因为在函数执行完之后函数中的变量s已经被释放了,所以自然s的引用也是空的,连引用的原始对象都没了,引用自然也就无意义了
如何更改呢?实际上直接返回String而不是&String就好了,这个操作实际上是所有权转移!
相当于:

fn main(){
    
    
//当然这样写肯定不是正确的我只是表达一下
	let a = let s = String::from("hello");
}

Slice字符串切片引用(&str)

首先理解字符串切片,我们知道字符串是复合类型,复合类型就能通过索引确定其中的一个元素(或称单元unit),切片就是在完整的String中截取[起始索引..终止索引],加个引用就是获取值而不取得所有权。突然一想是不是很熟悉,没错就是&str!

fn main() {
    
    
    let a = String::from("this is a test");
    let b = &a[1..6];
    println!("{}",b);
}

如果有一个字符串 slice,可以直接传递它。如果有一个 String,则可以传递整个 String 的 slice 或对 String 的引用。这种灵活性利用了 deref coercions 的优势,定义一个获取字符串 slice 而不是 String 引用的函数使得我们的 API 更加通用并且不会丢失任何功能

猜你喜欢

转载自blog.csdn.net/qq_51553982/article/details/130176138