通过切片澄清所有权

问题描述 投票:0回答:1

在 Rust 书中,他们给出了以下示例

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

    let word = first_word(&s); 

    s.clear(); 
    println!("the first word is: {}",word)
}

其中

first_word
定义为:

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}

编译器错误是

   Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
  --> src/main.rs:18:5
   |
16 |     let word = first_word(&s);
   |                           -- immutable borrow occurs here
17 |
18 |     s.clear(); // error!
   |     ^^^^^^^^^ mutable borrow occurs here
19 |
20 |     println!("the first word is: {}", word);
   |                                       ---- immutable borrow later used here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `ownership` (bin "ownership") due to 1 previous error

根据我对所有权的理解,

&s
引用了
s
,然后当它超出范围时被删除。当
first_word
函数返回时就会发生这种情况。然后我们得到一个新的引用,它专门指向内存中受切片影响的第一个字符,称为
word
。据我了解,
word
s
本质上都指向为字符串分配的相同内存块,但可以完全指向不同的地址。然而,当
s.clear()
被调用时(我假设)隐式地采用了对
s
的可变引用,称为
&self
&self
指向
s
word
指向其他东西。我的问题是,所有权规则是如何被违反的。据我了解,可变引用和不可变引用在同一范围内不能指向同一事物,但在这种情况下,不可变引用词和可变引用
&self
不应该指向同一事物?以图表形式我有 Diagram

rust
1个回答
0
投票

您缺少的是概念性的:当您使用一个引用派生另一个引用时,派生引用的生命周期不能比派生它的引用更长。这在函数签名中最为明显。

例如,让我们创建一个将引用作为输入并返回引用的函数:

fn foo(s: &str) -> &str

由于生命周期省略,这意味着相同的事情:

fn foo<'a>(s: &'a str) -> &'a str

酷。现在让我们实现它,但是我们不会从输入中导出返回的引用:

fn foo(_: &str) -> &str {
   "foo"
}

现在让我们使用它,给它一个从

String
派生的字符串切片的引用,清除
String
,然后打印返回的切片:

fn main() {
    let mut s = "bar".to_owned();
    let y = foo(&s);
    
    s.clear();
    println!("{y}");
}

此操作失败并出现相同的错误:

error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
  --> src/main.rs:9:5
   |
7  |     let y = foo(&s);
   |                 -- immutable borrow occurs here
8  |     
9  |     s.clear();
   |     ^^^^^^^^^ mutable borrow occurs here
10 |     println!("{y}");
   |               --- immutable borrow later used here

这样做的目的是为了说明函数的主体是完全不相关的。我们已经告诉编译器,返回的引用源自参数

s
中包含的引用。

引用是如何得出的或者它指向什么与此分析完全无关。

因此,示例代码中的内容是对字符串切片 (

word
) 的引用,该字符串切片源自
String
(
s
)。然后,您尝试通过调用
s
(此方法需要
s.clear()
)隐式获取对
&mut self
的独占引用,然后使用
word
。这是违规行为:
word
借用了
s
,即使它与
&s
并不指向同一事物,并且您试图同时专门借用
s

事实上,这个特定的示例准确地说明了为什么别名规则对于 Rust 如何实现内存安全是不可或缺的,即使在单线程代码中也是如此。如果允许编译此代码(例如,用 C++ 重写),那么在

word
之后访问
s.clear()
的内容将是一个经典的释放后使用错误。

这与您无法获取对

Vec
的元素的引用然后清除
Vec
的原因相同 - 因为对元素的引用源自对
Vec
的引用,对该元素借用了
Vec

© www.soinside.com 2019 - 2024. All rights reserved.