You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust中为Impl方法定义键值对形式的输入输出类型并实现模块解耦?

嘿,我来帮你解决这个Rust里定义键值对类型的问题!你之前用元组确实容易搞混字段顺序、可读性差,换成下面这些方案就能实现类似TypeScript接口的键值对形式,还能保持模块独立、避免耦合。

方案1:普通命名结构体(最推荐)

这是最直接的方式,每个输入/输出类型都定义成命名结构体,字段名称清晰,完全符合你要的键值对需求,而且每个模块的类型可以独立存放在自己的interfaces目录下。

目录结构保持你原来的设计:

.
├── src
│   ├── module1
│   │   ├── do_thing_a.rs
│   │   └── interfaces
│   │       ├── do_thing_a_input.rs
│   │       └── do_thing_a_output.rs
│   ├── module2
│   │   ├── do_thing_b.rs
│   │   └── interfaces
│   │       └── do_thing_b_output.rs
│   └── lib.rs (或 main.rs,用于声明模块)

具体代码实现:

首先在src/lib.rs(如果是二进制项目就用main.rs)里声明模块结构:

// src/lib.rs
mod module1 {
    pub mod interfaces {
        pub mod do_thing_a_input;
        pub mod do_thing_a_output;
    }
    pub mod do_thing_a;
}

mod module2 {
    pub mod interfaces {
        pub mod do_thing_b_output;
    }
    pub mod do_thing_b;
}

module1/interfaces/do_thing_a_input.rs

#[derive(Debug, Clone, Copy)]
pub struct DoThingAInput {
    pub value: i8,
}
  • 用#[derive]添加常用trait(比如Debug方便打印,Clone/Copy让结构体可以轻松复制)
  • pub修饰结构体和字段,让其他模块可以访问

module1/interfaces/do_thing_a_output.rs

#[derive(Debug, Clone, Copy)]
pub struct DoThingAOutput {
    pub value: bool,
}

module1/do_thing_a.rs

use crate::module1::interfaces::do_thing_a_input::DoThingAInput;
use crate::module1::interfaces::do_thing_a_output::DoThingAOutput;

pub struct DoThingA;

impl DoThingA {
    pub fn execute(&self, input: DoThingAInput) -> DoThingAOutput {
        DoThingAOutput {
            value: input.value == 1,
        }
    }
}

module2/interfaces/do_thing_b_output.rs

#[derive(Debug, Clone, Copy)]
pub struct DoThingBOutput {
    pub value: bool,
}

module2/do_thing_b.rs(解耦版本)

如果你需要模块间解耦(不直接依赖DoThingA的具体实现),可以通过抽象trait来调用,避免强耦合:
首先在src/traits.rs定义公共 trait:

// src/traits.rs
pub trait Executable<Input, Output> {
    fn execute(&self, input: Input) -> Output;
}

然后修改module1/do_thing_a.rs实现这个 trait:

use crate::traits::Executable;
use crate::module1::interfaces::do_thing_a_input::DoThingAInput;
use crate::module1::interfaces::do_thing_a_output::DoThingAOutput;

pub struct DoThingA;

impl Executable<DoThingAInput, DoThingAOutput> for DoThingA {
    fn execute(&self, input: DoThingAInput) -> DoThingAOutput {
        DoThingAOutput {
            value: input.value == 1,
        }
    }
}

最后module2/do_thing_b.rs只依赖 trait,不直接依赖DoThingA:

use crate::traits::Executable;
use crate::module1::interfaces::do_thing_a_input::DoThingAInput;
use crate::module1::interfaces::do_thing_a_output::DoThingAOutput;
use crate::module2::interfaces::do_thing_b_output::DoThingBOutput;

pub struct DoThingB;

impl DoThingB {
    pub fn execute(&self, executor: impl Executable<DoThingAInput, DoThingAOutput>) -> DoThingBOutput {
        let result = executor.execute(DoThingAInput { value: 1 });
        DoThingBOutput { value: result.value }
    }
}

主函数调用(src/main.rs)

use crate::module1::do_thing_a::DoThingA;
use crate::module2::do_thing_b::DoThingB;

fn main() {
    let do_thing_a = DoThingA;
    let do_thing_b = DoThingB;
    let result = do_thing_b.execute(do_thing_a);
    println!("Result: {}", result.value); // 输出 Result: true
}
方案2:带默认值的结构体(适合有可选字段的场景)

如果你的输入类型有可选字段,可以通过Default trait让创建实例更方便:

// module1/interfaces/do_thing_a_input.rs
#[derive(Debug, Clone, Copy, Default)]
pub struct DoThingAInput {
    pub value: i8,
    pub optional_flag: bool, // 可选字段,默认值为false
}

创建实例时可以只指定必填字段,其他用默认值:

let input = DoThingAInput { value: 1, ..Default::default() };
方案3:配合Serde实现序列化(适合API/跨语言场景)

如果需要将这些类型序列化成JSON等格式,可以用serde库,只需要添加derive宏:

首先在Cargo.toml添加依赖:

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

然后修改结构体:

// module1/interfaces/do_thing_a_input.rs
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub struct DoThingAInput {
    pub value: i8,
}

这样就能轻松转换成JSON:

let input = DoThingAInput { value: 1 };
let json = serde_json::to_string(&input).unwrap();
println!("JSON: {}", json); // 输出 JSON: {"value":1}

这些方案都能满足你“键值对形式、模块独立、低耦合”的需求,其中普通命名结构体是最通用的选择,可读性和可维护性都远高于元组。

内容的提问来源于stack exchange,提问作者Emille C.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 18:22:46