Как получить ссылку на деструктурированную структуру или провалиться? - PullRequest
0 голосов
/ 02 января 2019

У меня есть struct Boo внутри enum Foo. Я использую match, который проверяет различные варианты. Чтобы перейти к одному из вариантов, мне нужно деструктурировать Boo, но после перехода к нему мне нужна ссылка на него:

enum Foo {
    Boo(Boo),
    //other variants
}

struct Boo {
    field: Option<String>,
    //other fields
}

fn main() {
    let foo: Foo = unimplemented!();

    match foo {
        Foo::Boo(Boo {
            field: Some(ref name),
        }) if name.starts_with("moo") || name.starts_with("boo") => {
            // I need a reference to boo here
            unimplemented!();
        }
        _ => unimplemented!(),
    }
}

Обратите внимание, что в коде, скрытом _ => unimplemented!(), я также проверяю вариант Foo::Boo с другим name.starts_with и без if.

Возможно ли это? Если это невозможно, возможно, можно вернуть управление к match проверяющему пути кода после неудачного теста? Есть ли другой обходной путь?

Ответы [ 2 ]

0 голосов
/ 02 января 2019

Существует синтаксис Binding в сопоставлениях с образцами, к сожалению, не разрешено связывать внутри привязки ...

Связывание guard + может быть полезным, так как оно позволяет сопоставлять другие поля в Boo, имеющие ограниченную охрану:

fn has_expected_name(name: &str) -> bool {
    name.starts_with("boo") || name.starts_with("moo")
}

match foo {
    Foo::Boo(ref boo @ Boo { field: Some(_) })
        if has_expected_name(boo.field.as_ref().unwrap()) =>
    {
        unimplemented!("{:?}", boo);
    }
    _ => unimplemented!(),
}

Это дает понять, даже если определение has_expected_name немного далеко, что оно получает доступ только к boo.field и ничего больше.


Также возможно просто использовать привязку:

match foo {
    Foo::Boo(ref boo @ Boo {
        field: Some(_),
    }) if boo.field.unwrap().starts_with("moo") || boo.field.unwrap().starts_with("boo") => {
        unimplemented!("{:?}", boo);
    }
    _ => unimplemented!(),
}
0 голосов
/ 02 января 2019

Мой обходной путь для этих случаев - написать вспомогательную функцию для реализации защиты.Таким образом, вы можете захватить boo без деструктурирования и по-прежнему использовать защиту матча:

fn main() {
    let foo: Foo = unimplemented!();

    fn is_my_boo(boo: &Boo) -> bool {
        match boo.field {
            Some(ref name) => name.starts_with("boo") || name.starts_with("moo"),
            None => false
        }
    }

    match foo {
        Foo::Boo(ref boo) if is_my_boo(boo) => {
            //you can reference boo here
            unimplemented!();
        }
        _ => unimplemented!(),
    }
}

Вы также можете написать вспомогательную функцию как замыкание, если хотите, но для чистых функций я предпочитаю fnсинтаксис.

Если вы чувствуете, что локальные функции безобразны, вы можете попытаться объединить все тело функции в защите с обязательными дополнительными скобками:

fn main() {
    let foo: Foo = unimplemented!();

    match foo {
        Foo::Boo(ref boo) if {
                match boo.field {
                    Some(ref name) => name.starts_with("boo") || name.starts_with("moo"),
                    None => false
                }
            } => {
            //you can reference boo here
            unimplemented!();
        }
        _ => unimplemented!(),
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...