约束Rust方法以接受有效类型对的两个变量

我有一个具有一些公共字段的结构,但有两个字段必须包含相关类型。我还想实现一种new只允许组合有效对的方法。这将大大改善结构人体工程学。

为了说明这一点。我有一个名为 的结构Query,它具有以下字段:

#[derive(Debug)]
pub struct Query<V, U> {
    pub value: V,
    pub unit: U,
    pub name: String,
}

value可以是 ofTimeDistancetype,unit可以是 ofTimeUnitDistanceUnittype。

#[derive(Debug)]
pub struct Time;

#[derive(Debug)]
pub enum TimeUnit {
    Seconds,
    Hours,
    Days,
    Weeks,
    Months,
    Years,
}

#[derive(Debug)]
pub struct Distance;

#[derive(Debug)]
pub enum DistanceUnit {
    Meters,
    Kilometers,
    Miles,
}

拥有这样的结构使得实现new泛型方法变得容易:

impl Query<Time, TimeUnit> {
    pub fn new(value: Time, unit: TimeUnit, name: String) -> Self {
        Self { value, unit, name }
    }
}

impl Query<Distance, DistanceUnit> {
    pub fn new(value: Distance, unit: DistanceUnit, name: String) -> Self {
        Self { value, unit, name }
    }
}

然而,这留下了一个漏洞并允许手动创建这种结构:

/// Do not allow creating query with Time and DistanceUnit and vice-versa
fn do_not_allow_such_case() {
    let _ = Query {
        value: Time {},
        unit: DistanceUnit::Kilometers,
        name: "query".into(),
    };
}

另一种可能是更好的方法是使不可能的状态无法表示,因此我将通过引入具有允许对的 Enum 来重构结构:

#[derive(Debug)]
pub enum Pair {
    Time(Time, TimeUnit),
    Distance(Distance, DistanceUnit),
}

#[derive(Debug)]
pub struct Query2 {
    pub query: Pair,
    pub name: String,
}

我更喜欢这种方式,但是我很难new像以前的情况一样实现一种方法。我的第一个想法是From为先前声明的枚举实现特征并实现一个通用new方法,我可以在其中使用这些From特征作为约束:

impl From<(Time, TimeUnit)> for Pair {
    fn from(q: (Time, TimeUnit)) -> Self {
        Self::Time(q.0, q.1)
    }
}

impl From<(Distance, DistanceUnit)> for Pair {
    fn from(q: (Distance, DistanceUnit)) -> Self {
        Self::Distance(q.0, q.1)
    }
}
#[derive(Debug)]
pub enum ValueEnum {
    Time(Time),
    Distance(Distance),
}

impl From<Time> for ValueEnum {
    fn from(v: Time) -> Self {
        Self::Time(v)
    }
}

impl From<Distance> for ValueEnum {
    fn from(v: Distance) -> Self {
        Self::Distance(v)
    }
}
#[derive(Debug)]
pub enum UnitEnum {
    Time(TimeUnit),
    Distance(DistanceUnit),
}

impl From<TimeUnit> for UnitEnum {
    fn from(u: TimeUnit) -> Self {
        Self::Time(u)
    }
}

impl From<DistanceUnit> for UnitEnum {
    fn from(u: DistanceUnit) -> Self {
        Self::Distance(u)
    }
}

这是我尝试实现new方法,但它不会编译。

impl Query2 {
    pub fn new<V, U>(value: V, unit: U, name: String) -> Self
    where
        V: Into<ValueEnum>,
        U: Into<UnitEnum>,
        /// No idea how to constrain the pair
    {
        Self {
            /// This would not allow compiling
            query: (value, unit).into(),
            name,
        }
    }
}

有没有办法实现我想要的?

这是 Rust 游乐场的链接:https : //play.rust-lang.org/? version = stable&mode = debug&edition = 2018&gist =64ac6f5cb9b439e9943925c7ee8dd2e6

回答

我会使用特征和相关类型

use std::fmt::Debug;
pub trait Value {
    type Unit: Debug;
}

#[derive(Debug)]
pub struct Time;

impl Value for Time {
    type Unit = TimeUnit;
}

#[derive(Debug)]
pub enum TimeUnit {
    Seconds,
    Hours,
    Days,
    Weeks,
    Months,
    Years,
}

#[derive(Debug)]
pub struct Query<T: Value> {
    pub value: T,
    pub unit: <T as Value>::Unit,
    pub name: String,
}

impl<T: Value> Query<T> {
    pub fn new(value: T, unit: <T as Value>::Unit, name: String) -> Self {
        Self { value, unit, name }
    }
}

fn main() {
    let time_query: Query<Time> = Query::new(Time {}, TimeUnit::Seconds, "Seconds".to_owned());
}

游乐场链接

另一种将 value 和它的单位结合起来的更简单的方法是使 unit 成为 value 的成员。这种方法的优点是使 API 更小一些,但代价是这种单元类型基本上无法命名。

#[derive(Debug)]
pub struct Distance {
    unit: DistanceUnit,
}

#[derive(Debug)]
pub enum DistanceUnit {
    Meters,
    Kilometers,
    Miles,
}

#[derive(Debug)]
pub struct Query<T> {
    pub value: T,
    pub name: String,
}

impl Query<Distance> {
    pub fn new(value: Distance, name: String) -> Self {
        Self { value, name }
    }
}
fn main() {
    let time_query: Query<Distance> = Query::<Distance>::new(
        Distance {
            unit: DistanceUnit::Meters,
        },
        "Seconds".to_owned(),
    );
}

游乐场链接


以上是约束Rust方法以接受有效类型对的两个变量的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>