rust - 如何链接采用先前结果并满足严格顺序的函数?

标签 rust

示例:

let response = add_customer(InputCustomer)
    .validate()?
    .generate_code()
    .create(DB::create(pool_conextion))?;

我尝试使用各种结构,但我不知道这是否是最好的方法:

struct InputCustomer {}

fn add_customer(i: InputCustomer) -> Validate {
    Validate {
        result: InputCustomer {},
    }
}

struct Validate {
    result: InputCustomer,
}

impl Validate {
    fn do_validate() -> GenCode {
        // valdiate struct customer
        GenCode {
            result: InputCustomer {},
        }
    }
}

struct GenCode {
    result: InputCustomer,
}

impl GenCode {
    fn generate_code() -> Create {
        // generate customer code
        Create { result: true }
    }
}

struct Create {
    result: bool,
}

最佳答案

您可以使用phantom type parameters在单个结构上实现所有功能。 。 Customer 结构保存一些状态:

pub struct Customer<State> {
    state: PhantomData<State>,
}

我们可以创建客户可能处于的状态:

pub struct CustomerStateNew;
pub struct CustomerStateValidated;
pub struct CustomerStateWithCode;

当您创建客户时,它的状态为CustomerStateNew:

pub fn add_customer() -> Customer<CustomerStateNew> {
    Customer { state: PhantomData }
}

要验证客户,它必须处于CustomerStateNew状态:

impl Customer<CustomerStateNew> {
    pub fn validate(&self) -> Customer<CustomerStateValidated> {
        Customer { state: PhantomData }
    }
}

必须验证客户 (CustomerStateValidated) 才能生成代码:

impl Customer<CustomerStateValidated> {
    pub fn generate_code(&self) -> Customer<CustomerStateWithCode> {
        Customer { state: PhantomData }
    }
}

并且它必须具有要创建的生成代码 (CustomerStateWithCode)。 create 消耗 self,因此客户在创建后就无法使用(您可能不希望出现这种行为,但为了完整起见,我将其包含在此处):

impl Customer<CustomerStateWithCode> {
    pub fn create(self) -> Result<(), ()> {
        Ok(())
    }
}

现在我们可以将创建用户的方法链接在一起:

let result = add_customer().validate().generate_code().create()?;

但是,如果我们在验证之前尝试创建Customer,则代码将无法编译:

let result = add_customer().create();

// error[E0599]: no method named `create` found for struct `Customer<CustomerStateNew>`
//   --> src/main.rs:36:20
// 36 |     add_customer().create();
//   |                    ^^^^^^ method not found in `Customer<CustomerStateNew>`

此外,其他人都无法创建具有任意状态的 Customer,因为 state 字段是私有(private)的:

mod somewhere_else {
    fn bla() {
        let customer: Customer<CustomerStateWithCode> = Customer { state: PhantomData };
        customer.create();
    }
}

// error[E0451]: field `state` of struct `Customer` is private
//    --> src/main.rs:41:64
//    |
// 41 |  let customer: Customer<CustomerStateWithCode> = Customer { state: PhantomData };
//    |    

如果您想要存储特定于每个状态的数据,您可以将实际的State 存储在Customer 中,而不是PhantomData。然而现在,状态不仅仅是编译时安全,而且将在运行时存储:

pub struct CustomerStateWithCode(pub usize);

pub struct Customer<State> {
    state: State,
}

impl Customer<CustomerStateValidated> {
    pub fn generate_code(&self) -> Customer<CustomerStateWithCode> {
        Customer { state: CustomerStateWithCode(1234) }
    }
}

我们使用幻像类型创建了一个简单的状态机。这也称为类型状态模式。请注意,状态将被编译为空,因此没有运行时成本,只有编译时安全!

Playground link

关于rust - 如何链接采用先前结果并满足严格顺序的函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65311492/

相关文章:

rust - 如何在非异步函数上等待Rust Future

rust - 在Rust中检测ConnectionReset,而不是使线程处于紧急状态

rust - 配置/安装使用 Rust Cargo 作为构建系统的软件的预期/计划方式是什么?

python - 尝试释放 CString 数组时发生内存泄漏

rust - 如何为实现特定特征的所有类型批量实现反序列化?

reference - 每当删除对源数据的可变引用时,如何重新缓存数据?

rust - future 永远不要调用韦克可以吗

rust - 如何更改默认的 rustc/Cargo 链接器?

unit-testing - 测试自定义箱子

rust - 如何仅比较从同一父对象创建的对象?