Rust 中的引用与借用

这篇具有很好参考价值的文章主要介绍了Rust 中的引用与借用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1、引用与借用

 1.1 可变引用

1.2 悬垂引用

1.3 引用的规则

2、slice 类型

 2.1 字符串字面量其实就是一个slice

2.2 总结


1、引用与借用

在之前我们将String 类型的值返回给调用函数,这样会导致这个String会被移动到函数中,这样在原来的作用域不可访问了,但是我们功能一个String值得引用,这样就不会导致这个String类型的值被移动,而传递的只是一个引用。引用更像一个指针,因为是一个地址,我们就可以基于这个地址找到改地址上存储的数据。 与指针不同,引用确保指向某个特定类型的有效值。

下面是一个引用传递的示例:

fn main() {
    let str = String::from("hello world!");
    let len = _length(&str);
    println!("str is value: {}", str);
    println!("str length is: {}", len)
} 
fn _length(s: &String) ->usize {
    s.len()
}

 运行结果所示所示:

Rust 中的引用与借用,Rust,rust,前端,javascript

根据以上代码可以看出_length方法中传递的参数为&str,所以这里传递的是str值的引用,用&符号代表引用

以下是一张对应的示意图:

Rust 中的引用与借用,Rust,rust,前端,javascript

根据上图也能看出s是s1的引用,引用的是s1在堆中对应类型的值。

注意:与使用 & 引用相反的操作是 解引用dereferencing),它使用解引用运算符,*

变量 s 有效的作用域与函数参数的作用域一样,不过当 s 停止使用时并不丢弃引用指向的数据,因为 s 并没有所有权。当函数使用引用而不是实际值作为参数,无需返回值来交还所有权,因为就不曾拥有所有权。

我们将创建一个引用的行为称为 借用borrowing),因为我们并没有拥有它的所有权,只是暂时借用以下。

我们可以尝试修改一下引用,把引用值改了,看下是否可以,这就类似于我借了别人的东西,然后把东西换了个样子,看看是不是可以呢?

fn main() {
    let str = String::from("hello world!");
    let len = _length(&str);
    println!("str is value: {}", str);
} 
fn _length(s: &String) {
    s.push_str("我把你给改了..........");
}

运行一下,看下结果:

Rust 中的引用与借用,Rust,rust,前端,javascript

根据提示可以s是一个引用,因此它引用的数据不能作为可变数据借用。

 1.1 可变引用

允许我们修改一个借用的值,这就是 可变引用,把上面的示例改一下,如下所示:

fn main() {
    let mut str = String::from("hello world!");
    _length(&mut str);
    println!("str is value: {}", str);
} 
fn _length(s: &mut String) {
    s.push_str("我把你给改了..........");
}

运行代码,再看一下结果:

Rust 中的引用与借用,Rust,rust,前端,javascript

 首先定义str必须时可变的,在方法中传递参数,也要指定引用为可变引用,因为引用指向的是被引用的地址,所以就会改变原有的值。

注意:可变引用有一个很大的限制:如果你有一个对该变量的可变引用,你就不能再创建对该变量的引用。

 看以下示例:

fn main() {
    let mut str = String::from("hello world!");
    let a = &mut str;
    let b = &mut str;
    println!("a {}, b{}", a, b)
} 

Rust 中的引用与借用,Rust,rust,前端,javascript

 根据错误提示,可以知道同一时间不能多次借用str作为可变变量。Rust这样限制是因为可以在编译时就避免数据竞争。数据竞争data race)类似于竞态条件,它可由这三个行为造成:

  • 两个或更多指针同时访问同一数据。
  • 至少有一个指针被用来写入数据。
  • 没有同步数据访问的机制。

再看下以下示例:

fn main() {
    let mut str = String::from("hello world!");
    let c = &mut str;
    let b = &str;
    println!("b{} {}", b, c)
} 

Rust 中的引用与借用,Rust,rust,前端,javascript

 也会报错,借用和可变借用不能同时被使用。

 再看下一个示例:

fn main() {
    let mut str = String::from("hello world!");
    let c = &mut str;
   
    println!("{}", c);
    let b = &str;
    println!("{}", b)
} 

Rust 中的引用与借用,Rust,rust,前端,javascript

 可以看到这次是可以打印处结果的,在第一次打印的时候,变量的作用域也就结束了,因而在下次进行赋值时可以的。

1.2 悬垂引用

在具有指针的语言中,很容易通过释放内存时保留指向它的指针而错误地生成一个 悬垂指针dangling pointer),所谓悬垂指针是其指向的内存可能已经被分配给其它持有者。相比之下,在 Rust 中编译器确保引用永远也不会变成悬垂状态:当你拥有一些数据的引用,编译器确保数据不会在其引用之前离开作用域。

下面时一个悬垂应用的示例:

fn main() {
    dp();
} 
fn dp() -> &String {    // 返回字符串的引用
    let str = String::from("hello world!");   // 创建一个字符串
    &str      // 返回字符串的引用
}  // str 的作用域结束
// 方法返回的时字符串的引用,而字符串离开作用与,被释放,然在此返回该字符串的引用,
// 就会导致返回的结果不是预期的结果,在Rust中是不让这样操作的。

 直接运行,会报如下错误:

Rust 中的引用与借用,Rust,rust,前端,javascript

根据报错可知,此函数的返回类型包含借用值,但没有可供借用的值,在返回类型引用处,提示: 错误的声明周期修饰符。

我们改以下返回字符串本身,看一下结果怎么样?

fn main() {
    println!("value is {}", dp())
} 
fn dp() -> String {    
    let str = String::from("hello world!");   
    return str
}  

运行一下看看:

Rust 中的引用与借用,Rust,rust,前端,javascript

发现对应的值给打印出来,所有权交出去了,所以,可打印出对应的值。

1.3 引用的规则

根据之前的结果,我们可以总结出以下两点:

  • 在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用。
  • 引用必须总是有效的。

2、slice 类型

 slice 允许你引用集合中一段连续的元素序列,而不用引用整个集合。slice 是一类引用,所以它没有所有权。

以下有个slice示例:

fn main() {
        let s = String::from("hello world");
        let hello = &s[0..5];
        let world = &s[6..11];
        println!("{}---{}", hello, world)  
} 

运行以下,看下结果如何: 

Rust 中的引用与借用,Rust,rust,前端,javascript

根据以上结果,可以知道hello变量从字符串(hello world)中进行截取的,开始的位置为0,长度为5,所以打印的结果为hello,而world变量是从开始索引6开始,11结束,11-5=6,那么它的长度也是5,所以打印的结果为world.

Slice 的主要结果包括2部分:

  • 第一部分,是指针,指向数据开始的位置
  • 第二部分,是长度,就是元素结束减去开始位置的值

以下是一个示意图,能够更加清楚知道slice与字符串的关系:

Rust 中的引用与借用,Rust,rust,前端,javascript

其他写法,例如取前5个字符:

fn main() {
        let s = String::from("hello world");
        let hello = &s[0..5];
        let hello1 = &s[..5];  // hello 和 hello1 是等价的
        println!("{}---{}", hello, hello1)  
} 

例如取最后5个字符:

fn main() {
        let s = String::from("hello world");
        let world = &s[6..];
        let world1 = &s[6..];
        println!("{}---{}", world, world1)  
} 

取整个长度的切片:

fn main() {
        let s = String::from("hello world");
        let world = &s[..];
        let world1 = &s[..];
        println!("{}---{}", world, world1)  
} 

注意:字符串 slice range 的索引必须位于有效的字符边界内,如果尝试从超过边界访问超出索引范围将导致panic错误。

 2.1 字符串字面量其实就是一个slice

一个示例如下所示:

Rust 中的引用与借用,Rust,rust,前端,javascript

这里 s1 的类型是 &str:world 的类型也是&str,所以s1它是一个指向二进制程序特定位置的 slice。这也就是为什么字符串字面值是不可变的;&str 是一个不可变引用。

2.2 总结

所有权、借用和 slice 这些概念让 Rust 程序在编译时确保内存安全。Rust 语言提供了跟其他系统编程语言相同的方式来控制你使用的内存,但拥有数据所有者在离开作用域后自动清除其数据的功能意味着你无须额外编写和调试相关的控制代码。文章来源地址https://www.toymoban.com/news/detail-754577.html

到了这里,关于Rust 中的引用与借用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Rust in Action笔记 第四章生命周期、所有权、借用

    第四章用了一个行星通信的例子来阐述整个主题,主要角色有地面站(Ground station)、人造卫星(CubeSat),两者有不同的状态并且能互相发消息通信; Rust有类型安全(type safety)机制来检查函数的类型和返回值,如果一个自定义类型(用struct声明的)包含了非内置类型(如

    2024年02月09日
    浏览(31)
  • 【Rust 基础篇】Rust 解引用多态

    在 Rust 中,解引用多态(Deref Coercion)是一种特性,它允许自动进行类型转换,将实现了 Deref trait 的类型转换为目标类型的引用。通过解引用多态,我们可以更方便地使用不同类型的智能指针和引用。 本篇博客将详细介绍 Rust 中解引用多态的使用方法和相关概念,以及它在代

    2024年02月16日
    浏览(25)
  • 【Rust 基础篇】Rust 引用循环:解析和避免

    在 Rust 中,引用循环是指两个或多个对象之间相互引用,形成一个循环链。这种情况下,对象之间的引用计数永远不会变为零,导致内存泄漏和资源泄漏。本篇博客将详细介绍 Rust 引用循环的概念、问题和解决方案,并通过代码示例演示如何避免引用循环。 引用循环在 Rust

    2024年02月17日
    浏览(28)
  • Rust - 可变引用和悬垂引用

    在上一篇文章中,我们提到了 借用 的概念,将获取引用作为函数参数称为  借用 ( borrowing ),通常情况下,我们无法修改 借来的变量 ,但是可以通过 可变引用 实现修改 借来的变量 。代码示例如下: 要想实现修改 借来的变量 就必须将  s 改为  mut 。然后必须创建一个

    2024年01月22日
    浏览(29)
  • Rust-解引用

    “解引用”(Deref)是“取引用”(Ref)的反操作。取引用,我们有、mut等操作符,对应的,解引用,我们有 操作符,跟C语言是一样的。示例如下: 比如说,我们有引用类型p:i32;,那么可以用 符号执行解引用操作。上例中,v1的类型是i32,p的类型是i32,*p的类型又返回i32。 解引用操作

    2024年01月19日
    浏览(33)
  • rust 引用怎么用

    文章会通过三个例子来了解 rust 所有权的机制,都是从应用的角度来说明,rust 所有权底层是如何实现会在后续的内容中介绍。 理解 RUST 所有权规则: RUST 中的每个值都有一个对应的变量作为它的所有者 同一时间内,值有且仅有一个所有者 当所有者离开自己的作用域时,它

    2024年02月16日
    浏览(38)
  • rust学习-引用C库

    #[link(name = \\\"...\\\")] 是一个用于链接外部库的属性宏。 可以在 Rust 代码中引入其他语言编写的动态链接库( .so 、 .dll 等文件),从而实现 Rust 和其他语言的互操作。 #[link(name = \\\"...\\\")] 属性宏用于在 Rust 模块中引入标准 C 库(如 math.h 等)和其他外部库。 name 参数用于指定需要链

    2024年02月10日
    浏览(24)
  • 30天拿下Rust之引用

    概述         在Rust语言中,引用机制是其所有权系统的重要组成部分,它为开发者提供了一种既高效又安全的方式来访问和共享数据。引用可以被视为一个指向内存地址的指针,它允许我们间接地访问和操作存储在内存中的数据。与其他语言中的指针不同,Rust中的引用是

    2024年03月10日
    浏览(31)
  • Rust语法:所有权&引用&生命周期

    垃圾回收管理内存 Python,Java这类语言在管理内存时引用了一种叫做垃圾回收的技术,这种技术会为每个变量设置一个引用计数器(reference counter),来统计每个对象的引用次数。 一旦某个对象的引用数为0,垃圾回收器就会择取一个时机将其所占用的空间回收。 以Python为例子

    2024年02月12日
    浏览(42)
  • rust踩雷笔记(1)——切片传参和解引用赋值

    最近学习rust,网上资料还是很有限,做题遇到的问题,有时需要自己试验。把自己做题过程遇到的问题,和试验的结论,做一些简单记录。 阅读下列文字和代码 用切片(的引用)做参数要非常小心,切片中的某个元素直接用=赋值,用的是copy方式而不是所有权转移(实践证

    2024年02月12日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包