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

如何在Rust中创建仅部分字段可设默认值的结构体?

在Rust中实现部分字段必须显式定义的结构体

当然可以实现这个需求,下面提供几种实用的方案,覆盖编译时检查和灵活API的不同场景:

1. 状态式构建器(编译时强制检查必填字段)

通过分阶段的结构体设计,强制调用者必须设置必填字段才能完成构造,编译阶段就会拦截错误:

struct Person {
    name: String,
    age: usize,
    occupation: Option<String>,
}

// 初始状态:无任何必填字段
struct PersonBuilderEmpty;

// 已设置name的状态
struct PersonBuilderWithName {
    name: String,
}

// 已设置name和age的状态(可构造最终结构体)
struct PersonBuilderWithNameAndAge {
    name: String,
    age: usize,
    occupation: Option<String>,
}

impl PersonBuilderEmpty {
    fn new() -> Self {
        PersonBuilderEmpty
    }

    fn name(self, name: String) -> PersonBuilderWithName {
        PersonBuilderWithName { name }
    }
}

impl PersonBuilderWithName {
    fn age(self, age: usize) -> PersonBuilderWithNameAndAge {
        PersonBuilderWithNameAndAge {
            name: self.name,
            age,
            occupation: None,
        }
    }
}

impl PersonBuilderWithNameAndAge {
    fn occupation(mut self, occupation: String) -> Self {
        self.occupation = Some(occupation);
        self
    }

    fn build(self) -> Person {
        Person {
            name: self.name,
            age: self.age,
            occupation: self.occupation,
        }
    }
}

// 使用示例
fn main() {
    // 合法:完成所有必填字段设置
    let person_a = PersonBuilderEmpty::new()
        .name("A".to_string())
        .age(42)
        .build();

    // 编译失败:PersonBuilderWithName没有build方法,必须先调用age
    // let person_b = PersonBuilderEmpty::new()
    //     .name("B".to_string())
    //     .build();

    // 合法:设置所有必填字段+可选字段
    let person_c = PersonBuilderEmpty::new()
        .name("C".to_string())
        .age(42)
        .occupation("Software engineer".to_string())
        .build();
}

2. 宏实现(贴近字面量语法)

通过自定义宏强制要求必填参数,不符合要求时直接编译报错:

struct Person {
    name: String,
    age: usize,
    occupation: Option<String>,
}

impl Default for Person {
    fn default() -> Self {
        Person {
            name: String::default(), // 仅作为占位,宏会强制覆盖必填字段
            age: 0,
            occupation: None,
        }
    }
}

#[macro_export]
macro_rules! person {
    // 匹配必填字段+可选occupation的情况
    ($name:expr, $age:expr, $(, occupation: $occ:expr)?) => {
        Person {
            name: $name.to_string(),
            age: $age,
            $(occupation: Some($occ.to_string()),)?
            ..Default::default()
        }
    };
}

// 使用示例
fn main() {
    // 合法:仅设置必填字段
    let person_a = person!("A", 42);

    // 编译失败:宏要求至少传入name和age两个参数
    // let person_b = person!("B");

    // 合法:设置必填+可选字段
    let person_c = person!("C", 42, occupation: "Software engineer");
}

3. 普通构建器(运行时检查)

如果不需要编译时检查,追求更灵活的API,可以用带错误返回的构建器:

struct Person {
    name: String,
    age: usize,
    occupation: Option<String>,
}

struct PersonBuilder {
    name: Option<String>,
    age: Option<usize>,
    occupation: Option<String>,
}

impl PersonBuilder {
    fn new() -> Self {
        PersonBuilder {
            name: None,
            age: None,
            occupation: None,
        }
    }

    fn name(mut self, name: String) -> Self {
        self.name = Some(name);
        self
    }

    fn age(mut self, age: usize) -> Self {
        self.age = Some(age);
        self
    }

    fn occupation(mut self, occupation: String) -> Self {
        self.occupation = Some(occupation);
        self
    }

    fn build(self) -> Result<Person, &'static str> {
        Ok(Person {
            name: self.name.ok_or("必须设置name字段")?,
            age: self.age.ok_or("必须设置age字段")?,
            occupation: self.occupation,
        })
    }
}

// 使用示例
fn main() -> Result<(), &'static str> {
    // 合法
    let person_a = PersonBuilder::new()
        .name("A".to_string())
        .age(42)
        .build()?;

    // 运行时错误:未设置age
    // let person_b = PersonBuilder::new()
    //     .name("B".to_string())
    //     .build()?;

    // 合法
    let person_c = PersonBuilder::new()
        .name("C".to_string())
        .age(42)
        .occupation("Software engineer".to_string())
        .build()?;

    Ok(())
}

内容的提问来源于stack exchange,提问作者Nick

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 08:43:32