热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何解决可变和不可变借用的共存?

如何解决《如何解决可变和不可变借用的共存?》经验,为你挑选了1个好方法。

我有一个Context结构:

struct Context {
    name: String,
    foo: i32,
}

impl Context {
    fn get_name(&self) -> &str {
        &self.name
    }
    fn set_foo(&mut self, num: i32) {
        self.foo = num
    }
}

fn main() {
    let mut cOntext= Context {
        name: "MisterMV".to_owned(),
        foo: 42,
    };
    let name = context.get_name();
    if name == "foo" {
        context.set_foo(4);
    }
}

在一个函数中,我需要首先得到namecontextfoo根据name我的更新:

let name = context.get_name();
if (name == "foo") {
    context.set_foo(4);
}

代码将无法编译,因为get_name()take &selfset_foo()take &mut self.换句话说,我有一个不可变的借用,get_name()但我也在set_foo()同一范围内有可变借用,这违反了引用规则.

在任何给定的时间,您可以拥有(但不是两个)一个可变引用或任意数量的不可变引用.

错误看起来像:

error[E0502]: cannot borrow `context` as mutable because it is also borrowed as immutable
  --> src/main.rs:22:9
   |
20 |     let name = context.get_name();
   |                ------- immutable borrow occurs here
21 |     if name == "foo" {
22 |         context.set_foo(4);
   |         ^^^^^^^ mutable borrow occurs here
23 |     }
24 | }
   | - immutable borrow ends here

我想知道如何解决这种情况?



1> LinearZoetro..:

这是一个非常广泛的问题.借用检查器可能是Rust最有用的功能之一,也是最棘手的处理方式.人体工程学的改进正在定期进行,但有时会出现这种情况.

有几种方法可以解决这个问题,我会尝试回顾每个方法的优点和缺点:

I.转换为仅需要有限借款的表格

当你学习Rust时,你会慢慢了解借阅到期以及有多快.例如,在这种情况下,您可以转换为

if context.get_name() == "foo" {
    context.set_foo(4);
}

借用在if语句中到期.这通常是您想要的方式,并且随着非词汇生命周期等功能变得更好,此选项变得更加可口.例如,当NLL可用时,您当前编写它的方式将起作用,因为这种结构被正确地检测为"有限借用"!由于奇怪的原因(例如,如果声明需要连接可变和不可变的调用),重构有时会失败,但应该是您的首选.

II.使用带有表达式作为语句的作用域攻击

let name_is_foo = {
    let name = context.get_name();
    name == "foo"
};

if name_is_foo {
    context.set_foo(4);
}

Rust使用任意返回值的任意范围语句的能力非常强大.如果其他一切都失败了,你几乎总是可以使用块来限制借用,并且只返回一个非借用标志值,然后用于可变调用.方法I通常更清楚,但是这个方法很有用,清晰,并且惯用Rust.

III.在类型上创建"融合方法"

   impl Context {
      fn set_when_eq(&mut self, name: &str, new_foo: i32) {
          if self.name == name {
              self.foo = new_foo;
          }
      }
   }

当然,这有无穷无尽的变化.最常见的是一个函数,它接受一个fn(&Self) -> Option,并根据该闭包的返回值进行None设置(对于"不设置",Some(val)设置该值).

有时最好允许结构修改自己而不用"外部"逻辑.对于树木尤其如此,但在最坏的情况下会导致方法爆炸,当然如果在无法控制的外来类型上操作,则无法实现.

IV.克隆

let name = context.get_name().clone();
if name == "foo" {
    context.set_foo(4);
}

有时您必须快速克隆.尽可能避免这种情况,但有时候只是投入clone()某个地方而不是花费20分钟试图弄清楚如何使你的借用工作是值得的.取决于您的截止日期,克隆的成本,您调用该代码的频率,等等.

例如,可以说PathBuf在CLI应用程序中过度克隆s并不是非常罕见的.

V.使用不安全(不推荐)

let name: *const str = context.get_name();
unsafe{
    if &*name == "foo" {
        context.set_foo(4);
    }
}

这应该几乎从来没有被使用,但在极端情况下可能是必要的,或在情况下的性能,其中你基本上被迫克隆(这可以用图形或一些靠不住的数据结构发生).总是,总是尽量避免这种情况,但请将其保存在工具箱中,以备不时之需.

请记住,编译器期望您编写的不安全代码支持安全Rust代码所需的所有保证.一个unsafe块表示虽然编译器无法验证代码是否安全,但程序员有.如果程序员没有正确验证它,编译器可能会产生包含未定义行为的代码,这可能导致内存不安全,崩溃等等,Rust努力避免的许多事情.


推荐阅读
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 如何在跨函数中使用内存?
    本文介绍了在跨函数中使用内存的方法,包括使用指针变量、动态分配内存和静态分配内存的区别。通过示例代码说明了如何正确地在不同函数中使用内存,并提醒程序员在使用动态分配内存时要手动释放内存,以防止内存泄漏。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • 设计模式——模板方法模式的应用和优缺点
    本文介绍了设计模式中的模板方法模式,包括其定义、应用、优点、缺点和使用场景。模板方法模式是一种基于继承的代码复用技术,通过将复杂流程的实现步骤封装在基本方法中,并在抽象父类中定义模板方法的执行次序,子类可以覆盖某些步骤,实现相同的算法框架的不同功能。该模式在软件开发中具有广泛的应用价值。 ... [详细]
author-avatar
摄影师张恒
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有