rust从入门到放弃(六):借用

上一篇文章介绍了所有权,所有权是对数据有完全的操作权限,你对自己的手机有完全的所有权一样,你甚至可以选择销毁这个手机。但有时候,朋友可能借用一下你的手机打个电话,这时候,你仍然保持手机的所有权,只是给别人借用了一下。

rust从入门到放弃(六):借用

我们可以看到一个复杂类型,比如String、Vec等,在赋值给另外一个变量后,就无法使用了。譬如下面的代码就会报错,如果还没搞清楚的,请回看上一篇文章。

fn foo(mut v: Vec) {
    v.push(5);
}
fn main() {
    let v = vec![];
    foo(v);
    println!("{:?}",v)
}

但日常的编程场景中,一个变量又会在多处使用。为了解决这个问题,rust 引入了一个新的概念:借用。我们可以通过借用修改一下上面的代码

fn foo(v: &mut Vec) { //参数类似是一个 Vec 的可变借用
    v.push(5);
}
fn main() {
    let mut v = vec![];
    foo(&mut v); //借用
    foo(&mut v); //再次借用
    println!("{:?}",v) // 输出 [5, 5]
}

rust 里面的 & 代表借用,意思就是借用一下,当foo函数执行完成后,又还回来了,整个过程所有权没有发生转移(move)。而且还可以多次借用,上面故意写了两个 foo 函数调用。可变借用允许修改数据内容,就像你把手机借给别人,别人可能会重置部分手机设置一样。如果你不想让别人修改,可以使用不可变借用,把 mut 去掉。譬如我们只需要只读的方式 print 打印内容。如下:

fn main() {
    let mut v = vec![];
    foo(&mut v);
    foo(&mut v);
    print(&v); // 不可变借用 [5, 5]
    print(&v); //不可变借用  [5, 5]
}

fn print(v:  &Vec) {
   println!("{:?}",v);
}

那么这个借用是如何实现的呢?譬如下面这段代码

fn main() {
    let s1 = String::from("hello"); //s1 拥有所有权

    let len = calculate_length(&s1); //s 借用

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

他们在内存的实现如下:s 代表借用,借用的本质是指向所有权的指针。

rust从入门到放弃(六):借用

所以这里有一点非常重要,借用的生命的周期必须小于借用对象的生命周期,如果s1 生命的终止了,s 已经没有意义了。譬如下面的代码

fn main() {
   let mut s1 = String::from("hello");
    println!("len {}",s1.len()); //不可变借用 等效于 len(self: &String)
    s1.push('!'); //可变借用 
    println!("len {}",s1.len()); //不可变借用 
    let v = s1; //转移(move)所有权了
    println!("len {}",s1.len()); // 不可变借用出错
}

上面的代码最后一行就会报错,因为s1 所有权已经转移了,后续就不能借用了,这个其实也很好理解,当你把手机卖给了张三后,你就不能再借给李四使用了。

关于借用还有一个限制:&mut 可变借用同时只能存在一个,而不可变可以同时存在多个。这里为啥强调同时呢? 上面的例子已经可以看到,当多次执行foo 函数使用 &mut 可变借用追加元素的时候,没有报错,因为这些追加操作并非是同时发生的。而下面代码就会报错

fn main() {
    let mut x = 1_i32;
    let p = &mut x;
    let v = &x; // 或者 let x =2 重新赋值 都不允许
    println!("value of pointed : {}", p);
}

因为 p 这个借用的声明周期,是到最后一行打印才结束的。在这个p 的周期内,这个对象是冻结的,不允许发生其他借用或者修改其内容。这个其实也很好理解,主要就是为了考虑并发场景中数据的一致性。当你把手机借给李四了,你就无法再修改手机配置或者同时再借给张三了。但是多个不可变借用是可以同时存在的,因为他们之间是没有数据冲突的,都是只读的。譬如下面的代码,多个不可变借用是可以共存的。

fn main() {
    let mut x = 1_i32;
    let p = &x;
    let v = &x;
    println!("value of pointed : {}", p);
}

其实这个和读写锁本质上是一致的,如果某个线程获取了写锁,那么后续其他线程的读或者写都将被阻塞,但如果只是获得读锁,其他线程的写互斥,但读却并不阻塞。

展开阅读全文

页面更新:2024-05-12

标签:可能会   赋值   线程   变量   所有权   函数   周期   入门   发生   操作   代码   生命   代表   内容   数据   手机   科技

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top