在向下转换后将具有多个值的枚举模式匹配时使用移动的值

时间:2017-05-02 16:10:58

标签: rust pattern-matching

我可以在enum上使用具有一个String参数的模式匹配:

extern crate robots;

use std::any::Any;
use robots::actors::{Actor, ActorCell};

#[derive(Clone, PartialEq)]
pub enum ExampleMessage {
    Msg { param_a: String },
}

pub struct Dummy {}

impl Actor for Dummy {
    // Using `Any` is required for actors in RobotS
    fn receive(&self, message: Box<Any>, _context: ActorCell) {
        if let Ok(message) = Box::<Any>::downcast::<ExampleMessage>(message) {
            match *message {
                ExampleMessage::Msg { param_a } => println!("got message"),
            }
        }
    }
}

然而我无法在带有2个参数的枚举上执行模式匹配:

#[derive(Clone, PartialEq)]
pub enum ExampleMessage {
    Msg { param_a: String, param_b: usize },
}

impl Actor for Dummy {
    // Using `Any` is required for actors in RobotS
    fn receive(&self, message: Box<Any>, _context: ActorCell) {
        if let Ok(message) = Box::<Any>::downcast::<ExampleMessage>(message) {
            match *message {
                ExampleMessage::Msg { param_a, param_b } => println!("got message"),
            }
        }
    }
}

这会导致错误:

error[E0382]: use of moved value: `message`
  --> src/example.rs:19:48
   |
19 |                 ExampleMessage::Msg { param_a, param_b } => {
   |                                       -------  ^^^^^^^ value used here after move
   |                                       |
   |                                       value moved here
   |
   = note: move occurs because `message.param_a` has type `std::string::String`, which does not implement the `Copy` trait

我之前在同一enum尝试了模式匹配而没有向下转换,这很好但我需要向下转发。 这对我来说似乎很奇怪,我不知道如何规避这个错误。

我正在使用Rust 1.19.0-nightly(afa1240e5 2017-04-29)

1 个答案:

答案 0 :(得分:4)

  

我尝试在同一个枚举上进行模式匹配而不进行向下转换,这样可以正常工作

这是减少问题的一个很好的尝试。问题是你减少了太多。将Box<T>向下转换为Foo并不会返回Fooit returns a Box<Foo>

fn downcast<T>(self) -> Result<Box<T>, Box<Any + 'static>> 

您可以使用以下方法重现问题:

#[derive(Clone, PartialEq)]
pub enum ExampleMessage {
    Msg { param_a: String, param_b: usize },
}

fn receive2(message: Box<ExampleMessage>) {
    match *message {
        ExampleMessage::Msg { param_a, param_b } => println!("got message"),
    }
}

fn main() {}

好消息

这是借用检查程序当前实现的限制,并且在启用non-lexical lifetimes时原始代码将按原样运行:

#![feature(nll)]

#[derive(Clone, PartialEq)]
pub enum ExampleMessage {
    Msg { param_a: String, param_b: usize },
}

fn receive2(message: Box<ExampleMessage>) {
    match *message {
        ExampleMessage::Msg { param_a, param_b } => println!("got message"),
    }
}

fn main() {}

当前的现实

非词汇生命周期和基于MIR的借阅检查器尚不稳定!

当您与取消引用的值匹配时,该值 not 正常移动。这allows you to do something like

enum Foo {
    One,
    Two,
}

fn main() {
    let f = &Foo::One;
    match *f {
        Foo::One => {}
        Foo::Two => {}
    }
}

在这种情况下,您希望获取Box 1 中的内容的所有权,以便在match中对字段进行解构时获取字段的所有权。您可以通过在尝试匹配之前将值移出框来实现此目的。

这样做的好方法是:

fn receive2(message: Box<ExampleMessage>) {
    let message = *message;
    match message {
        ExampleMessage::Msg { param_a, param_b } => println!("got message"),
    }
}

但你也可以使用花括号强制移动:

fn receive2(message: Box<ExampleMessage>) {
    match {*message} {
        ExampleMessage::Msg { param_a, param_b } => println!("got message"),
    }
}

我不完全理解为什么单个字段会起作用;它确实不一致。我唯一的 guess Box的所有权被移动到第一个参数,提取了参数,然后编译器再次尝试将其移动到下一个参数。

1 - 通过*移出包含的元素是一种特殊功能,只有Box支持。例如,如果您尝试使用引用执行此操作,则会得到&#34;无法移出借来的内容&#34;错误。您也无法实现Deref特征来执行此操作;它是编译器内部的硬编码能力。