4

Rust を見ていると、よくわからない動作に気付きました。

私は期待通りに動作するこのコードを持っています:

fn get_or_create_foo(v: &mut Vec<String>) -> String {
    match v.get(0) {
        Some(x) => return x.clone(),
        None => ()
    }

    println!("creating foo");
    v.push("foo".to_string());
    v.get(0).unwrap().clone()
}

fn main() {
    let mut v = Vec::new();
    println!("{}", get_or_create_foo(&mut v));
    println!("{}", get_or_create_foo(&mut v));
}

get_or_create_foo()借用した文字列スライスを返すように変更すると、コンパイラはコンパイルを拒否します。

fn get_or_create_foo(v: &mut Vec<String>) -> &str {
    match v.get(0) {
        Some(x) => return x,
        None => ()
    }

    println!("creating foo");
    v.push("foo".to_string());
    v.get(0).unwrap()
}

コンパイル ログ:

$ rustc --verbose src/main.rs
src/main.rs:8:5: 8:6 error: cannot borrow `*v` as mutable because it is also borrowed as immutable
src/main.rs:8     v.push("foo".to_string());
                  ^
src/main.rs:2:11: 2:12 note: previous borrow of `*v` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `*v` until the borrow ends
src/main.rs:2     match v.get(0) {
                        ^
src/main.rs:10:2: 10:2 note: previous borrow ends here
src/main.rs:1 fn get_or_create_foo(v: &mut Vec<String>) -> &str {
...
src/main.rs:10 }
               ^
error: aborting due to previous error

私の理解では、コードは有効です。前述の借用はmatch、コードの変更につながるパスを使用して制御が句を離れるとすぐに返される可能性がありますv

私が間違っている?そのようなコードを許可すると問題が発生する場合、誰かが例を挙げてもらえますか?

4

3 に答える 3

2

swizard のソリューションを少し改善できます。

fn get_or_create_foo(v: &mut Vec<String>) -> &str {
    if v.is_empty() {
        println!("creating foo");
        v.push("foo".to_string());        
    }

    &v[0]
}
于 2015-03-07T06:20:42.997 に答える
1

私もRustは初めてですが、あなたの問題の原因を見つけたかもしれません。

ここで「get」関数の型シグネチャを調べることができます。ご覧のとおり、「get」関数は、要求されたベクトルのメンバー (Option 内にラップされている) への借用参照を返します。私の推測では、コンパイラは、「x」が一致ブロックから「エスケープ」できないことを確認できません。

以下は、 A 30-minute Introduction to Rustからのより単純ですが、同様の例です:

fn main() {
   let mut v = vec![];

   v.push("Hello");

   let x = &v[0];

   v.push("world");

   println!("{}", x);
}

Rust では、型システムが所有権の概念をエンコードします。変数 v はベクトルの所有者です。v を参照するときは、その変数 (この場合は x) にしばらく借りさせます。あなたが本を持っていて、それを私に貸してくれるのと同じように、私はその本を借りています。

したがって、push の 2 回目の呼び出しでベクターを変更しようとするときは、そのベクターを所有している必要があります。しかし、xはそれを借りています。他人に貸したものを変更することはできません。そのため、Rust はエラーをスローします。

これが私がそれをイメージしている方法です:

fn get_or_create_foo(v: &mut Vec<String>) -> &str {
    let a: &str;

    match v.get(0) {
        Some(x) => {
            a = x;
            return x;
        },
        None => ()
    }

    // Now "a" is still borrowing "v" immutably!
    // println!("{:?}", a);

    println!("creating foo");
    v.push("foo".to_string());
    v.get(0).unwrap()
}

私が言ったように、私はまだ初心者なので、これにはもっとあるかもしれません。あなたのコードを少しいじってから、私は結論に達しました。

簡単なリファクタリングでこの問題を解決できます:

fn get_or_create_foo(v: &mut Vec<String>) -> &str {
    match v.get(0) {
        // Notice how the borrowed value is never used and
        // thus can not "escape" our match block.
        Some(_) => (),
        _       => v.push("foo".to_string())
    }
    
    // No need to use "get" here since we are 100% sure that
    // the indexed vector contains at least one item.
    return &v[0];
}
于 2015-03-06T20:56:51.123 に答える