返回

如何在 Rust 中干净地实现“瀑布”逻辑

发布时间:2022-07-21 21:35:44 273
# scala

我有一个类型的集合,代表我的数据模式的各种旧版本和新版本:

struct Version1;
struct Version2;
struct Version3;
struct Version4;

这些类型可以在彼此之间迁移,一次迁移一个:

impl Version1 { fn migrate_to_v2(self) -> Version2 { Version2 } }
impl Version2 { fn migrate_to_v3(self) -> Version3 { Version3 } }
impl Version3 { fn migrate_to_v4(self) -> Version4 { Version4 } }

我有一个包含所有这些版本的枚举,这是我从磁盘读取的数据格式:

enum Versioned {
    V1(Version1),
    V2(Version2),
    V3(Version3),
    V4(Version4),
}

我想写一个函数来执行Versioned反对Version4. 有多种方法可以做到这一点,但都有明显的缺陷;我的问题是,还有其他我忽略的解决方案吗?

  • 直接调用方法。问题是指数膨胀;随着我在该产品的生命周期内添加更多版本,该代码的大小将按二次方放大:
fn migrate_versioned(versioned: Versioned) -> Version4 {
    match versioned {
        V1(data) => data.migrate_to_v2().migrate_to_v3().migrate_to_v4(),
        V2(data) => data.migrate_to_v3().migrate_to_v4(),
        V3(data) => data.migrate_to_v4(),
        V4(data) => data,
    }
}
  • 使用一系列链接if lets、 这需要遍历枚举类型,并且我们失去了对match:
fn migrate_versioned(mut versioned: Versioned) -> Version4 {
    use Versioned::*;

    if let V1(data) = versioned {
        versioned = V2(data.migrate_to_v2());
    }

    if let V2(data) = versioned {
        versioned = V3(data.migrate_to_v3());
    }

    if let V3(data) = versioned {
        versioned = V4(data.migrate_to_v4());
    }

    if let V4(data) = versioned {
        return data;
    }

    unreachable!();
}
  • 使用loop. 这将恢复在中丢失的穷举性检查if letif let:
fn migrate_versioned(mut versioned: Versioned) -> Version4 {
    loop {
        versioned = match versioned {
            V1(data) => V2(data.migrate_to_v2()),
            V2(data) => V3(data.migrate_to_v3()),
            V3(data) => V4(data.migrate_to_v4()),
            V4(data) => break data,
        };
    }
}
  • 一些连锁特征。这比其他的都好,但事实并非如此非常样板上沉重:
trait ToV2: Sized {
    fn migrate_to_v2(self) -> Version2;
}

impl ToV2 for Version1 { ... }

trait ToV3: Sized {
    fn migrate_to_v3(self) -> Version3;
}

impl ToV3 for Version2 { ... }
impl ToV3 for T {
    fn migrate_to_v3(self) -> Version3 { self.migrate_to_v2().migrate_to_v3() }{
}

trait ToV4: Sized {
    fn migrate_to_v4(self) -> Version4;
}

impl ToV4 for Version3 { ... }
impl ToV4 for T {
    fn migrate_to_v4(self) -> Version3 { self.migrate_to_v3().migrate_to_v4() }{
}

fn migrate_versioned(versioned: Versioned) -> Version4 {
    match versioned {
        V1(data) => data.migrate_to_v4(),
        V2(data) => data.migrate_to_v4(),
        V3(data) => data.migrate_to_v4(),
        V4(data) => data,
    }
}

我在这里遗漏了什么解决方案吗?从概念上讲,这样做似乎很简单:

// V1 starts here
let v2 = v1.migrate_to_v2();
// V2 starts here
let v3 = v2.migrate_to_v3();
// V3 starts here
let v4 = v3.migrate_to_v4();
// v4 starts here

但我一点也不清楚如何(如果有的话)表达如此简单的解决方案的控制流。

特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们。
举报
评论区(1)
按点赞数排序
用户头像