[rust学习笔记]引用与借用

本文介绍了Rust中的引用概念,包括如何通过引用传递参数而不转移所有权,引用的解引用操作。接着讲解了借用的原理及默认的不可变性,以及如何创建可变引用并理解其在作用域中的唯一性限制。此外,文章还讨论了悬垂引用及其可能导致的问题,强调了Rust在防止悬垂引用方面的设计考虑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引用

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

    let len = calculate_length(&s1);

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

fn calculate_length(s: &String) -> usize {//函数签名中&代表这是一个引用参数
    s.len()
}

上面的代码中,calculate_length(s: &String)使用了String的引用传递参数,而没有直接转移值的所有权。在调用函数时也需要传递参数的引用。
这些&代表的就是引用语义,它们允许你在不获取所有权的情况下使用值。

注意: 与&操作相反的是 * 操作,* 代表解引用

在这里插入图片描述

借用

上面这种通过引用传递参数给函数的方法也被称为借用,借用来的值默认不能被修改,否则会报错,编译无法通过:

//错误代码
fn main() {
    let s = String::from("hello");

    change(&s);
}

fn change(some_string: &String) {
    some_string.push_str(", world");
}

可变引用

我们可用过一些调整来使借用来的值可变:可变引用。

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

    change(&mut s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

我们需要做三个步骤,写三个mut:

  1. 将变量设置为可变 mut
  2. 将引用实参设置为可变 &mut
  3. 将引用形参设置为可变 &mut

在可变引用的使用上有一个很大的限制: 在一个作用域中,同时只能存在一个可变引用。
以下为错误代码:

//错误代码
fn main() {
    let mut s = String::from("hello");

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

    println!("{}, {}", r1, r2);
}

这种限制可以帮助我在编译时避免数据竞争。数据竞争和竞态条件十分类似,它会在指令满足以下条件时发生:

  • 两个或以上的指针访问同一空间
  • 至少一个指针在空间中写入数据
  • 没有同步数据访问机制

rust中可以使用花括号来创造作用域,所以可以使两个可变引用在两个不同的作用域中:

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

    {
        let r1 = &mut s;
    } // r1 goes out of scope here, so we can make a new reference with no problems.

    let r2 = &mut s;
}

对于引用还有一个限制: 不可以在有不可变引用时,使用可变引用。

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

    let r1 = &s; // no problem
    let r2 = &s; // no problem
    let r3 = &mut s; // BIG PROBLEM

    println!("{}, {}, and {}", r1, r2, r3);
}

因为程序员大部分时候都不希望自己的只读值突然发生改变。

总结两个限制: 在同一作用域中,如果存在一个可变引用,则不能使用其他任何引用。

悬垂引用

悬垂引用比较类似野指针,指的是指向曾经存在某处内存地址,但该内存已经被释放甚至重新分配另作他用了。
在rust的设计中,能保证这个数据不会再引用被销毁前离开自己的作用域。(这句话挺绕的,我的理解就是引用指向的数据始终都是有效的,也一直是你指向的那个,不会改变)

如下是错误代码:

//错误代码
fn main() {
    let reference_to_nothing = dangle();
}

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

    &s
}

其实,这部分只需要把引用去掉就可以使用了。如果一定要用引用会涉及生命周期,这在后面讨论它。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值