Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Setters

Every field annotated with #[moxy(build)] gets a fluent setter method on the generated builder. Setters accept any value that implements Into<T>, so callers are not forced to construct the exact field type.

Note

All missing-field errors are compile errors, not runtime panics. The typestate guarantees that build() cannot be called until every required field has been set.

V: Into<T> Signature

use moxy::Build;

#[derive(Build, Default)]
struct Server {
    #[moxy(build)]
    pub host: String,
    #[moxy(build)]
    pub port: u16,
}

// &str is accepted because &str: Into<String>
let s = Server::new().host("localhost").port(8080_u16).build();
assert_eq!(s.host, "localhost");

Required setters consume the builder and return a new type, advancing the typestate. Optional setters (fields with a default) mutate in place and return Self.

Partial Annotation

Only annotate the fields you want in the builder. Fields without #[moxy(build)] are not exposed as setters — they receive Default::default() when build() is called:

use moxy::Build;

#[derive(Build, Default)]
struct Connection {
    #[moxy(build)]
    pub host: String,
    #[moxy(build)]
    pub port: u16,
    pub timeout: u64,   // not in builder — gets 0u64
}

let conn = Connection::new().host("127.0.0.1").port(5432_u16).build();
assert_eq!(conn.timeout, 0u64);

Required vs Optional Fields

A field annotated with bare #[moxy(build)] is requiredbuild() is not available until all required fields are set. Forgetting one is a compile error:

use moxy::Build;

#[derive(Build, Default)]
struct Config {
    #[moxy(build)]
    pub host: String,
    #[moxy(build)]
    pub port: u16,
}

// error[E0599]: no method named `build` found for struct `ConfigBuilder<true>`
//  --> src/main.rs:10:35
//   |
//   | Config::new().host("localhost").build();
//   |                                 ^^^^^ method not found in `ConfigBuilder<true>`
//   |
//   = note: the method was found for
//           - `ConfigBuilder<true, true>`
Config::new().host("localhost").build();

Caution

Setting a required field twice is also a compile error — the setter is consumed after first use and the updated builder type no longer has that method.

Setting a required field twice is also a compile error — the setter is consumed after use:

use moxy::Build;

#[derive(Build, Default)]
struct Config {
    #[moxy(build)]
    pub host: String,
    #[moxy(build)]
    pub port: u16,
}

// error[E0599]: no method named `host` found for struct `ConfigBuilder<true>`
//   |
//   | Config::new().host("a").host("b").port(80_u16).build();
//   |                         ^^^^ method not found in `ConfigBuilder<true>`
//   |
//   = note: method `host` is available on `ConfigBuilder`
Config::new().host("a").host("b").port(80_u16).build();

To make a field optional, provide a fallback with default = <expr> or use an Option<T> type.

Option<T> Fields

Fields with an Option<T> type are automatically optional — no default attribute needed. The setter accepts the inner type T and wraps it in Some:

use moxy::Build;

#[derive(Build, Default)]
struct Profile {
    #[moxy(build)]
    pub name: String,
    #[moxy(build)]
    pub bio: Option<String>,
}

// bio is optional — defaults to None
let p = Profile::new().name("alice").build();
assert_eq!(p.bio, None);

// setter accepts &str (not Option<&str>) and wraps in Some
let p = Profile::new().name("alice").bio("hello").build();
assert_eq!(p.bio, Some("hello".to_string()));

Any Order

Required setters can be called in any order:

use moxy::Build;

#[derive(Build, Default)]
struct Config {
    #[moxy(build)]
    pub host: String,
    #[moxy(build)]
    pub port: u16,
}

let a = Config::new().host("localhost").port(8080_u16).build();
let b = Config::new().port(8080_u16).host("localhost").build();

assert_eq!(a.host, b.host);
assert_eq!(a.port, b.port);