2015-03-06 2 views
4

Взглянув на Руста, я заметил поведение, которое я не совсем понимаю.Заявление о матче с явным возвратом заимствованной ссылки

У меня этот код, который работает, как ожидалось:

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.

Я не прав? Может ли кто-нибудь привести пример, когда такой код может вызвать проблемы?

ответ

1

Я новичок в Rust себя, но я полагаю, что я мог бы найти источник проблемы.

Вы можете проверить подпись типа функции «получить» here. Как вы можете видеть, функция «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); 
} 

В Руст, система типа кодирует понятие собственности. Переменная v является владельцем вектора. Когда мы делаем ссылку на v, мы позволяем этой переменной (в данном случае x) заимствовать ее на некоторое время. Точно так же, как если у вас есть книга, и вы предоставляете ее мне, я заимствую книгу.

Итак, когда я пытаюсь изменить вектор со вторым вызовом, чтобы нажать, мне нужно владеть им. Но x заимствует его. Вы не можете изменить то, что вы одолжили кому-то. И поэтому Руста выдает ошибку.

Вот как я его визуализации:

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]; 
} 
2

Я не знаю точно, но я подозреваю, что ваш код:

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() 
} 

переводится компилятором в нечто с эквивалентным синтаксисом путем устранения явного return, как это:

fn get_or_create_foo(v: &mut Vec<String>) -> &str { 
    match v.get(0) { 
     Some(x) => x, 
     None => { 
      println!("creating foo"); 
      v.push("foo".to_string()); 
      v.get(0).unwrap() 
     }, 
    } 
} 

который, очевидно, терпит неудачу с той же ошибкой. Здесь get производит Option<&String>, поэтому v остается заимствованным даже в ветке None, где отсчет не производится.

К счастью, есть простой способ переписать функцию:

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

    v.get(0).unwrap() 
} 
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] 
} 
Смежные вопросы