2015-11-07 1 views
0

У меня есть код, подобный:Как получить доступ к полям, связанным с переменной в выражении соответствия?

use std::string::{String}; 
use std::vec::{Vec}; 

enum State { 
    A { 
     n: usize, 
     lines: Vec<String>, 
    }, 
    B { 
     n: usize, 
    } 
} 

fn main() { 
    use State::*; 

    let lines = vec!["a", "b", "GO", "c", "GO", "d"]; 
    let mut state = B { n: 0 }; 
    for line in &lines { 
     state = match state { 
      A { n, lines } => { 
       if line == &"GO" { 
        B { n: n + 1 } 
       } else { 
        let mut new_lines = Vec::from(lines); 
        new_lines.push(line.to_string()); 
        A { n: n, lines: new_lines } 
       } 
      }, 
      B { n } => { 
       A { n: n, lines: vec![line.to_string()] } 
      }, 
     }; 
    } 
    let final_n = match state { 
     A { n, .. } => n, 
     B { n } => n, 
    }; 
    println!("final_n = {}", final_n); 
} 

Rust Playground ссылке: (. Заметим, что это упрощение реального кода См first revision этого вопроса для полного фона.) http://is.gd/0QTYaQ

Я хочу, чтобы не создавать вектор new_lines, поэтому я попытался привязать значение State::A к переменной и получить доступ к полям значения так:

  s @ A { .. } => { 
       if line == &"GO" { 
        B { n: s.n + 1 } 
       } else { 
        s.lines.push(line.to_string()); 
        s 
       } 
      }, 

Однако это не удается скомпилировать:

 
ParseState_enum_test.rs:23:28: 23:31 error: attempted access of field `n` on type `State`, but no field with that name was found 
ParseState_enum_test.rs:23      B { n: s.n + 1 } 
                 ^~~ 
ParseState_enum_test.rs:19:5: 33:6 note: in this expansion of for loop expansion 
ParseState_enum_test.rs:25:21: 25:28 error: attempted access of field `lines` on type `State`, but no field with that name was found 
ParseState_enum_test.rs:25      s.lines.push(line.to_string()); 
               ^~~~~~~ 
ParseState_enum_test.rs:19:5: 33:6 note: in this expansion of for loop expansion 
error: aborting due to 2 previous errors 

Как получить доступ к полям значения, связанного с переменной?

EDIT: Я знаю ref mut в привязке к шаблону, но я не думаю, что это хорошее решение в моем случае. Если я использую ref mut, то мне нужно, чтобы создать клон вектора, потому что этот код не компилируется:

  A { n, ref mut lines } => { 
       if line == &"GO" { 
        B { n: n + 1 } 
       } else { 
        lines.push(line.to_string()); 
        A { 
         n: n, 
         lines: lines, // error: mismatched types 
        } 
       } 
      }, 
+1

I ** гарантия ** Вы можете [сделать меньший пример] (HTTP: // есть.gd/363IR1) :-) Это называется [MCVE] (/ help/mcve) и является очень хорошей идеей при задании вопроса о переполнении стека! Также обратите внимание, что наличие кода, который работает на [Rust Playground] (https://play.rust-lang.org/), является ** сильно ** предпочтительным. – Shepmaster

+0

Это все еще очень долго по сравнению с вопросом, который вы задаете. – Shepmaster

ответ

4

Следующее, похоже, работает. Решает ли проблема?

let new_state = match state { 
     B {n} => A { n: n, lines: vec![line.to_string()] }, 
     A {n, mut lines} => { 
      match *line { 
       "GO" => B { n: n + 1 }, 
       _ => { 
        lines.push(line.to_string()); 
        A{ n:n, lines: lines} 
       } 
      } 
     } 
    }; 
    state = new_state 

https://play.rust-lang.org/?gist=4fa712834999e45ccd4d&version=stable

+0

Да, это работает! Большое спасибо. –

2

Давайте посмотрим на более простую версию вашего вопроса:

enum Foo { 
    Alpha { score: u8 }, 
    Beta { lives_left: u8 }, 
} 

fn main() { 
    let the_value = Foo::Alpha { score: 42 }; 
    match the_value { 
     alpha_only @ Alpha => println!("Score is {}", alpha_only.score), 
     _ => println!("Dunno what to do!"), 
    } 
} 

Проблема в том что варианты перечисления не являются отдельными типами. То есть не имеет значения, чтобы иметь переменную типа Foo::Alpha; это может быть только тип Foo. Вы можете увидеть это в сообщении об ошибке:

попытка доступа поле scoreпо типу Foo, но не поле с таким именем не было найдено

При использовании @ связать всю картину, вы можете только знать, что получаете что-то типа Foo.

нормальный способ борьбы с этим связываться с компонентом элемента с помощью ref:

match the_value { 
    Foo::Alpha { ref score } => println!("Score is {}", score), 
    _ => println!("Dunno what to do!"), 
} 

И если вам нужно мутировать значение, используйте ref mut:

match the_value { 
    Foo::Alpha { ref mut score } => { 
     *score += 1; 
     println!("Score is {}", score) 
    }, 
    _ => println!("Dunno what to do!"), 
} 

Из если вы можете использовать это значение, вам не нужно ref:

let the_value = Foo::Alpha { score: 42 }; 
let new_value = match the_value { 
    Foo::Alpha { score } => Foo::Alpha { score: score + 1 }, 
    Foo::Beta { lives_left } => Foo::Alpha { score: lives_left * 2 }, 
}; 
+0

Это слишком упрощает мой вопрос и, таким образом, не отвечает на него. Я уже знал о 'ref mut' и о том, как это обычно используется. Проблема здесь в том, что мне нужно вернуть обновленное состояние, содержащее вектор. Я не могу просто вернуть новое значение 'A' с помощью' line' mut ref, потому что 'A' нуждается в фактическом векторе. См. Редактирование на мой вопрос. –

+0

@ DanielTrebbien, тогда ваш вопрос, похоже, не имеет никакого отношения к сопоставлению с образцом или привязке. Вы хотите «не создавать вектор« new_lines »и не хотите« создавать клон вектора », но вы просто ** не можете ** изменять данные, которые вы выполняете, потому что тело вашего цикла * doesn «Это». Вы можете просто изменить «на строку в строках» и использовать входной вектор? – Shepmaster

+0

Я не изменяю данные, которые я выполняю, и я согласен, что в Rust запрещено. Здесь новое значение 'state' создается с каждой итерацией цикла, а прежнее значение' state' перезаписывается. Я могу создать изменяемые значения для каждой из переменных, находящихся в состоянии (например, http://is.gd/AMTPgi); однако, это не очень элегантно. –

Смежные вопросы