首页 > 解决方案 > 在没有特征(move_ref_pattern)或 mem::replace 的情况下处理具有组合移动和参考的模式的策略是什么?

问题描述

如果不使用该功能#![feature(move_ref_pattern)],那么在模式中同时处理移动和引用匹配的策略是什么?

通常,我有一个例程通过引用传递参数和通过移动传递参数。我想匹配它们之间的元组,因为它让我更容易看到不同的组合。同时,我想适当地使用第二个参数中的内存,因为它已经由例程拥有。

目前,我处理它的方式是匹配一个可变引用,然后调用std::mem::replace以访问该值。这对我来说感觉很笨拙,我想看看是否有更好的方法来处理这个问题。

在这个大多数人为的示例中,该结构F64用于防止被调用:Copy

// Cloneable, but not copyable float type
#[derive(Clone, Debug)]
enum MyFloat {
    Zero,
    Num(F64),
}

#[derive(Clone, Debug)]
struct F64(f64);

// Consume y, but not x
fn my_add(x: &MyFloat, mut y: MyFloat) -> MyFloat {
    match (x, &mut y) {
        (MyFloat::Zero, MyFloat::Zero) => MyFloat::Zero,
        (MyFloat::Zero, y @ MyFloat::Num(_)) => std::mem::replace(y, MyFloat::Zero),
        (MyFloat::Num(_), MyFloat::Zero) => x.clone(),
        (MyFloat::Num(x), MyFloat::Num(y)) => {
            let y = std::mem::replace(y, F64(0.));
            MyFloat::Num(my_add_driver(x, y))
        }
    }
}

fn my_add_driver(x: &F64, y: F64) -> F64 {
    F64((*x).0 + y.0)
}

// Run the program
fn main() {
    let x = MyFloat::Num(F64(1.2));
    let y = MyFloat::Num(F64(2.3));
    println!("{:?} + {:?} = {:?}", x, y.clone(), my_add(&x, y));
}

在这里,y作为可变引用传递给匹配,这解决了模式匹配的移动引用问题。但是,我们必须使用std::mem::replace. 有没有更好的方法来处理这个?

标签: rust

解决方案


您可以像这样稍微简化它:

fn my_add(x: &MyFloat, y: MyFloat) -> MyFloat {
    // match on y by value, not by ref:
    match (x, y) {
        (MyFloat::Zero, MyFloat::Zero) => MyFloat::Zero,
        (MyFloat::Zero, y @ MyFloat::Num(_)) => y,
        (MyFloat::Num(_), MyFloat::Zero) => x.clone(),
        (MyFloat::Num(x), MyFloat::Num(ref mut y)) => {
            let y = std::mem::replace(y, F64(0.));
            MyFloat::Num(my_add_driver(x, y))
        }
    }
}

mem::replace()只有使用该move_ref_pattern功能或​​使用类似于以下的辅助方法才能摆脱其他调用Option::as_ref()

impl MyFloat {
    fn as_ref_option(&self) -> Option<&F64> {
        match self {
            MyFloat::Zero => None,
            MyFloat::Num(f) => Some(f),
        }
    }
}

// Consume y, but not x
fn my_add(x: &MyFloat, y: MyFloat) -> MyFloat {
    match (x.as_ref_option(), y) {
        (None, MyFloat::Zero) => MyFloat::Zero,
        (None, y @ MyFloat::Num(_)) => y,
        (Some(_), MyFloat::Zero) => x.clone(),
        (Some(x), MyFloat::Num(y)) => MyFloat::Num(my_add_driver(x, y)),
    }
}

游乐场链接


推荐阅读