AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 77164545
Accepted
NoBullsh1t
NoBullsh1t
Asked: 2023-09-24 03:07:07 +0800 CST2023-09-24 03:07:07 +0800 CST 2023-09-24 03:07:07 +0800 CST

什么时候同时拥有可变和不可变引用确实是一个问题?

  • 772

我已经玩 Rust 一段时间了。这很难,很新,很令人兴奋,而且编译器非常有帮助。所以我通常会得到工作代码。但有一个问题我根本不明白。这就是为什么拥有多个可变引用,或者一些不可变引用和一个可变引用是一个问题?

首先,在顺序执行的程序中,我不认为这会成为问题。假设我有两个引用,无论是可变的还是不可变的。在任何时间点,只有一件事会使用该引用。当然,该值可能会改变,但我不明白这里会如何出现内存不安全。

唯一可以以任何方式相关的方法(我可以想象)是当我们引入多个线程时。我会更放心地知道所有这些都只读取数据,因为在我读取数据时,其他线程无法篡改该值。这个假设正确吗?整个可变性处理是否仅与多线程上下文相关(除了在其他语言(如JSlet和constJS)中也存在的代码可预测性)?如果不是,顺序程序怎么会因为数据在不应该可变的情况下可变而陷入麻烦(如 Rust 所见)?

第二个问题似乎更引人注目:当然,我一次只能有一个可变引用,或者在拥有不可变引用的同时没有一个可变引用。但数据的所有者仍然存在!他可以随时改变它,特别是当另一个生成的线程通过不可变引用读取数据时(如果可能的话,我对此表示怀疑,因为生命周期检查)。当所有者仍然保留改变数据的权利时,通过禁止对其他引用数据的可变引用来增加什么样的安全性?

正如你所看到的,这些问题对于我理解 Rust 在内存安全方面添加的内容非常重要。不幸的是,我没有找到任何明确的解释来解释为什么这样做,只有这样。

rust
  • 2 2 个回答
  • 59 Views

2 个回答

  • Voted
  1. Best Answer
    prog-fh
    2023-09-24T03:16:23+08:002023-09-24T03:16:23+08:00

    对于您的第一个问题,这个简单的 Python 示例可以提供帮助

    def remove_unexpected_element(sequence, unexpected):
        for (idx, elem) in enumerate(sequence):
            if elem == unexpected:
                sequence.pop(idx)
    
    seq = [10, 10, 20, 20, 30, 30]
    remove_unexpected_element(seq, 20)
    print(seq) # [10, 10, 20, 30, 30]
    

    了解 Python 中序列迭代的详细工作原理的程序员第一眼就能发现这是不正确的。但如果我们不知道这些细节,我们就不知道这段代码是否做了它看起来要做的事情。也许另一种语言可以按预期执行此循环...(请注意,Java 会执行运行时检查,checkForComodification()以便检测这种情况并引发异常)使用 Rust,我们根本无法表达这种不明确的情况,因为我们尝试当我们仍在咨询数据时(在所有的过程中)改变数据(序列)迭代)。如果我们真的想表达这样的算法,我们必须使用整数显式计数并使用索引访问序列。我们不必对序列进行长时间的咨询(在迭代期间),而是必须执行多个不同的访问(检查长度、删除元素......),并且我们有责任保持所有这些一致。如果这没有按预期工作,那是因为我们算法的源代码中存在明显可读的错误。

    对于第二个问题,可变引用与其引用的可变值同时存在,但在使用此引用的代码部分中,无法直接访问该值。这正是借用检查器所负责的。当涉及多线程时,您将无法将独占(可变)引用传递给另一个线程,同时仍然能够在没有同步机制的情况下在其原始位置使用原始值。

    • 1
  2. jthulhu
    2023-09-24T05:07:36+08:002023-09-24T05:07:36+08:00

    主要问题是共享的可写内存不好。Rust 会尽其所能来防止这种情况发生。指向内存区域的指针要么是未分配的(即,没有其他指针指向该内存区域),要么是不可变的(即,无法通过该指针写入该内存区域)。虽然我刚才说的是 Rust 使用的模型的近似值,但足以理解其背后的动机。

    举个例子,下面的程序是用C

    void add_twice_1(int *a, int *b) {
        *a += *b;
        *a += *b;
    }
    

    它需要两个指向整数的指针,并将第二个指针的内容与第一个指针的内容相加两次。现在,您可能会认为该程序等同于以下程序:

    void add_twice_2(int *a, int *b) {
        *a += *b * 2;
    }
    

    然而,事实上,这两个程序实际上并不相同,如果a和b是相同的指针:

    void main() {
        int i = 3;
        add_twice_1(&i, &i);
        // `i` is now 12
    
        int j = 3;
        add_twice_2(&j, &j);
        // `j` is now 9!
    }
    

    正如您所看到的,问题来自这样一个事实:当add_twice_1调用时,指针a既用于写入指向的内存区域,又由 别名b。

    现在,这个问题的原因是编译器本质上是将您编写的代码转换为机器运行的代码的工具。他们必须确保的一个重要属性是这两个代码的行为相同。为此,他们必须能够推理程序,而使用共享可变指针则要困难得多(顺便说一句,这对于人类来说也是如此)。例如,如果您要使用 Rust 编写该代码,您将执行以下操作:

    fn add_twice(a: &mut i32, b: &i32) {
        *a += *b;
        *a += *b;
    }
    

    a必然是一个可变借用,因此是无别名的,因此 Rust 编译器可以像我上面所做的那样自由地优化这个函数(反之亦然)。

    • 1

相关问题

  • 在匹配内重用函数时,匹配臂具有预期的不兼容类型

  • match 语句中的 Rust 类型转换

  • 如何强制匹配的返回类型为()?

  • 原始表示中的 Rust 枚举

  • 有没有办法直接简化 Result<String, VarError> 中 Ok("VAL") 的匹配

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    使用 <font color="#xxx"> 突出显示 html 中的代码

    • 2 个回答
  • Marko Smith

    为什么在传递 {} 时重载解析更喜欢 std::nullptr_t 而不是类?

    • 1 个回答
  • Marko Smith

    您可以使用花括号初始化列表作为(默认)模板参数吗?

    • 2 个回答
  • Marko Smith

    为什么列表推导式在内部创建一个函数?

    • 1 个回答
  • Marko Smith

    我正在尝试仅使用海龟随机和数学模块来制作吃豆人游戏

    • 1 个回答
  • Marko Smith

    java.lang.NoSuchMethodError: 'void org.openqa.selenium.remote.http.ClientConfig.<init>(java.net.URI, java.time.Duration, java.time.Duratio

    • 3 个回答
  • Marko Smith

    为什么 'char -> int' 是提升,而 'char -> Short' 是转换(但不是提升)?

    • 4 个回答
  • Marko Smith

    为什么库中不调用全局变量的构造函数?

    • 1 个回答
  • Marko Smith

    std::common_reference_with 在元组上的行为不一致。哪个是对的?

    • 1 个回答
  • Marko Smith

    C++17 中 std::byte 只能按位运算?

    • 1 个回答
  • Martin Hope
    fbrereto 为什么在传递 {} 时重载解析更喜欢 std::nullptr_t 而不是类? 2023-12-21 00:31:04 +0800 CST
  • Martin Hope
    比尔盖子 您可以使用花括号初始化列表作为(默认)模板参数吗? 2023-12-17 10:02:06 +0800 CST
  • Martin Hope
    Amir reza Riahi 为什么列表推导式在内部创建一个函数? 2023-11-16 20:53:19 +0800 CST
  • Martin Hope
    Michael A fmt 格式 %H:%M:%S 不带小数 2023-11-11 01:13:05 +0800 CST
  • Martin Hope
    God I Hate Python C++20 的 std::views::filter 未正确过滤视图 2023-08-27 18:40:35 +0800 CST
  • Martin Hope
    LiDa Cute 为什么 'char -> int' 是提升,而 'char -> Short' 是转换(但不是提升)? 2023-08-24 20:46:59 +0800 CST
  • Martin Hope
    jabaa 为什么库中不调用全局变量的构造函数? 2023-08-18 07:15:20 +0800 CST
  • Martin Hope
    Panagiotis Syskakis std::common_reference_with 在元组上的行为不一致。哪个是对的? 2023-08-17 21:24:06 +0800 CST
  • Martin Hope
    Alex Guteniev 为什么编译器在这里错过矢量化? 2023-08-17 18:58:07 +0800 CST
  • Martin Hope
    wimalopaan C++17 中 std::byte 只能按位运算? 2023-08-17 17:13:58 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve