From 636a8c5618d0cf75c7181ada09e610c2e609ca09 Mon Sep 17 00:00:00 2001 From: Vitalii Kryvenko Date: Mon, 28 Oct 2024 02:52:41 +0200 Subject: [PATCH] Split API docs reference for `#[builder]` into multiple pages (#164) --- website/.vitepress/config.mts | 157 +- website/.vitepress/theme/style.css | 4 +- .../blog/bon-builder-generator-v2-release.md | 2 +- website/blog/bon-builder-v2-1-release.md | 4 - website/blog/bon-builder-v2-2-release.md | 7 +- website/blog/bon-builder-v2-3-release.md | 12 +- website/guide/alternatives.md | 4 +- website/guide/compatibility.md | 33 +- website/guide/inspecting.md | 4 +- website/guide/limitations.md | 4 - website/guide/optional-members.md | 7 +- website/guide/overview.md | 12 +- .../guide/patterns/conditional-building.md | 4 - .../patterns/into-conversions-in-depth.md | 4 - .../guide/patterns/shared-configuration.md | 2 +- website/guide/positional-members.md | 8 +- website/reference/builder.md | 1270 +---------------- website/reference/builder/member/default.md | 210 +++ website/reference/builder/member/finish-fn.md | 109 ++ website/reference/builder/member/into.md | 103 ++ website/reference/builder/member/name.md | 62 + website/reference/builder/member/skip.md | 66 + website/reference/builder/member/start-fn.md | 106 ++ .../builder/top-level/builder-type.md | 95 ++ website/reference/builder/top-level/derive.md | 141 ++ .../reference/builder/top-level/finish-fn.md | 63 + website/reference/builder/top-level/on.md | 137 ++ .../reference/builder/top-level/start-fn.md | 175 +++ website/v1/guide/compatibility.md | 4 - website/v1/guide/limitations.md | 4 - website/v1/guide/optional-members.md | 4 - website/v1/guide/overview.md | 4 - website/v1/reference/builder.md | 4 - website/v2/guide/compatibility.md | 4 - website/v2/guide/limitations.md | 4 - website/v2/guide/optional-members.md | 5 - website/v2/guide/overview.md | 4 - .../v2/guide/patterns/conditional-building.md | 4 - .../patterns/into-conversions-in-depth.md | 4 - website/v2/reference/builder.md | 4 - 40 files changed, 1405 insertions(+), 1449 deletions(-) create mode 100644 website/reference/builder/member/default.md create mode 100644 website/reference/builder/member/finish-fn.md create mode 100644 website/reference/builder/member/into.md create mode 100644 website/reference/builder/member/name.md create mode 100644 website/reference/builder/member/skip.md create mode 100644 website/reference/builder/member/start-fn.md create mode 100644 website/reference/builder/top-level/builder-type.md create mode 100644 website/reference/builder/top-level/derive.md create mode 100644 website/reference/builder/top-level/finish-fn.md create mode 100644 website/reference/builder/top-level/on.md create mode 100644 website/reference/builder/top-level/start-fn.md diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts index 9df01f8f..ac2fdd0f 100644 --- a/website/.vitepress/config.mts +++ b/website/.vitepress/config.mts @@ -12,7 +12,43 @@ export default defineConfig({ cleanUrls: true, lastUpdated: true, + vite: { + plugins: [ + { + name: "inject-abbreviations", + transform: { + order: "pre", + handler(src, id) { + if (!id.endsWith(".md")) { + return; + } + + const abbrs = { + Member: "Struct field or function argument", + member: "Struct field or function argument", + members: "Struct fields or function arguments", + ["starting function"]: + "Function that creates the builder (e.g. `builder()`)", + ["finishing function"]: + "Method on the builder struct that finishes building (e.g. `build()` or `call()`)", + }; + + const abbrsStr = Object.entries(abbrs) + .map(([key, value]) => `*[${key}]: ${value}`) + .join("\n"); + + return `${src}\n\n${abbrsStr}`; + }, + }, + }, + ], + }, + markdown: { + languageAlias: { + 'attr': 'js', + }, + theme: { dark: "dark-plus", light: "light-plus", @@ -155,84 +191,75 @@ export default defineConfig({ ], "/reference": [ { - text: "Reference", + text: "#[derive(Builder)] / #[builder]", + link: "/reference/builder", items: [ { - text: "#[derive(Builder)] / #[builder]", - link: "/reference/builder", + text: "Top-level", + link: "/reference/builder#top-level-attributes", items: [ { - text: "Top-level attributes", - link: "/reference/builder#top-level-attributes", - items: [ - { - text: "builder_type", - link: "/reference/builder#builder-type", - }, - { - text: "derive", - link: "/reference/builder#derive", - }, - { - text: "expose_positional_fn", - link: "/reference/builder#expose-positional-fn", - }, - { - text: "finish_fn", - link: "/reference/builder#finish-fn", - }, - { - text: "on", - link: "/reference/builder#on", - }, - { - text: "start_fn", - link: "/reference/builder#start-fn", - }, - ], + text: "builder_type", + link: "/reference/builder/top-level/builder-type", + }, + { + text: "derive", + link: "/reference/builder/top-level/derive", + }, + { + text: "finish_fn", + link: "/reference/builder/top-level/finish-fn", }, { - text: "Member attributes", - link: "/reference/builder#member-attributes", - items: [ - { - text: "default", - link: "/reference/builder#default", - }, - { - text: "finish_fn", - link: "/reference/builder#finish-fn-1", - }, - { - text: "into", - link: "/reference/builder#into", - }, - { - text: "name", - link: "/reference/builder#name", - }, - { - text: "skip", - link: "/reference/builder#skip", - }, - { - text: "start_fn", - link: "/reference/builder#start-fn-1", - }, - ], + text: "on", + link: "/reference/builder/top-level/on", + }, + { + text: "start_fn", + link: "/reference/builder/top-level/start-fn", }, ], }, { - text: "#[bon]", - link: "/reference/bon", - }, - { - text: "Other items on docs.rs", - link: "https://docs.rs/bon/latest/bon/", + text: "Member", + link: "/reference/builder#member-attributes", + items: [ + { + text: "default", + link: "/reference/builder/member/default", + }, + { + text: "finish_fn", + link: "/reference/builder/member/finish-fn", + }, + { + text: "into", + link: "/reference/builder/member/into", + }, + { + text: "name", + link: "/reference/builder/member/name", + }, + { + text: "skip", + link: "/reference/builder/member/skip", + }, + { + text: "start_fn", + link: "/reference/builder/member/start-fn", + }, + ], }, ], }, + { + text: "#[bon]", + link: "/reference/bon", + }, + { + text: "Other items on docs.rs", + link: "https://docs.rs/bon/latest/bon/", + }, ], }, }, diff --git a/website/.vitepress/theme/style.css b/website/.vitepress/theme/style.css index 041bbcc7..7f6553dc 100644 --- a/website/.vitepress/theme/style.css +++ b/website/.vitepress/theme/style.css @@ -1,5 +1,5 @@ -/* Prevent wrapping of inline code snippets */ -code, .nobr { +/* Opt-in nowrap via a class */ +.nobr { white-space: nowrap; } diff --git a/website/blog/bon-builder-generator-v2-release.md b/website/blog/bon-builder-generator-v2-release.md index 39f26735..d029403d 100644 --- a/website/blog/bon-builder-generator-v2-release.md +++ b/website/blog/bon-builder-generator-v2-release.md @@ -74,7 +74,7 @@ This has been a topic of [controversy](https://github.com/elastio/bon/issues/15) The main reason for removing this is to make `bon` more obvious and intuitive. Rust's core pillar is "being explicit". By having automatic `Into` conversions `bon` v1 introduced magical implicit behaviour, that also could lead to some footguns. For a detailed explanation of the potential footguns, see the ["Into Conversions In-Depth"](../guide/patterns/into-conversions-in-depth) page. -Now, if you want to enable `Into` conversions for a set of members, you can use the new [`#[builder(on(type_pattern, into))]`](../reference/builder#on) attribute. It allows you to specify the type that you want to enable `Into` conversions for explicitly. +Now, if you want to enable `Into` conversions for a set of members, you can use the new [`#[builder(on(type_pattern, into))]`](../reference/builder/top-level/on) attribute. It allows you to specify the type that you want to enable `Into` conversions for explicitly. Let's review an example of a migration that you need to do when upgrading to `bon` v2. diff --git a/website/blog/bon-builder-v2-1-release.md b/website/blog/bon-builder-v2-1-release.md index 5f7a676e..058279bf 100644 --- a/website/blog/bon-builder-v2-1-release.md +++ b/website/blog/bon-builder-v2-1-release.md @@ -261,7 +261,3 @@ You can leave comments for this post on the platform of your choice: [`bon`]: https://github.com/elastio/bon [`typed-builder`]: https://docs.rs/typed-builder/latest/typed_builder/ - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/blog/bon-builder-v2-2-release.md b/website/blog/bon-builder-v2-2-release.md index 70d1d328..e4fe22e9 100644 --- a/website/blog/bon-builder-v2-2-release.md +++ b/website/blog/bon-builder-v2-2-release.md @@ -59,7 +59,7 @@ cargo fmt ### Derive `Clone` and `Debug` for the builder -A new attribute is now supported at the top level. You can add [`#[builder(derive(...))]`](../reference/builder#derive) to ask `bon` to generate implementations of `Clone` or `Debug` for the builder. +A new attribute is now supported at the top level. You can add [`#[builder(derive(...))]`](../reference/builder/top-level/derive) to ask `bon` to generate implementations of `Clone` or `Debug` for the builder. This helps with reusing [partial builders](../guide/patterns/conditional-building#shared-partial-builder), because now you can clone the builder where only part of the fields are set. @@ -236,8 +236,3 @@ You can leave comments for this post on the platform of your choice: [`bon`]: https://github.com/elastio/bon [@recatek]: https://github.com/recatek - - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/blog/bon-builder-v2-3-release.md b/website/blog/bon-builder-v2-3-release.md index 76e6df7a..9d8447dd 100644 --- a/website/blog/bon-builder-v2-3-release.md +++ b/website/blog/bon-builder-v2-3-release.md @@ -32,8 +32,8 @@ As an example, suppose we have a `Treasure` struct with `x` and `y` coordinates To do that we can use the `#[builder(start_fn)]` attribute. There are two contexts where we can place it, and they both have a different meaning: -- [Top-level `#[builder(start_fn = ...)]`](../reference/builder#start-fn) - configures the name of the starting function and optionally its visibility -- [Member-level `#[builder(start_fn)]`](../reference/builder#start-fn-1) - configures the member to be a positional parameter on the starting function +- [Top-level `#[builder(start_fn = ...)]`](../reference/builder/top-level/start-fn) - configures the name of the starting function and optionally its visibility +- [Member-level `#[builder(start_fn)]`](../reference/builder/member/start-fn) - configures the member to be a positional parameter on the starting function We'll want to use both of these attributes in our example to give a better name for the starting function that describes its inputs and configure `x` and `y` as positional parameters on the starting function as well. @@ -78,7 +78,7 @@ impl Treasure { Now let's say we need to know the person who claimed the `Treasure`. While describing the treasure using the current builder syntax we'd like the person who claimed it to specify their first name and last name at the end of the building process. -We can use a similar combination of the [top-level `#[builder(finish_fn = ...)]`](../reference/builder#finish-fn) and the [member-level `#[builder(finish_fn)]`](../reference/builder#finish-fn-1) attributes to do that. +We can use a similar combination of the [top-level `#[builder(finish_fn = ...)]`](../reference/builder/top-level/finish-fn) and the [member-level `#[builder(finish_fn)]`](../reference/builder/member/finish-fn) attributes to do that. **Example:** @@ -115,7 +115,7 @@ assert_eq!(treasure.claimed_by_first_name, "Lyra"); // [!code highlight] assert_eq!(treasure.claimed_by_last_name, "Heartstrings"); // [!code highlight] ``` -You may also combine these attributes with [`#[builder(into)]`](../reference/builder#into) or [`#[builder(on(..., into))]`](../reference/builder#into) to reduce the number of `to_owned()` calls a bit. See this described in detail on the new ["Positional members"](../guide/positional-members#into-conversions) page in the guide. +You may also combine these attributes with [`#[builder(into)]`](../reference/builder/member/into) or [`#[builder(on(..., into))]`](../reference/builder/top-level/on) to reduce the number of `to_owned()` calls a bit. See this described in detail on the new ["Positional members"](../guide/positional-members#into-conversions) page in the guide. ### Guaranteed MSRV is 1.59.0 now @@ -154,7 +154,3 @@ You can leave comments for this post on the platform of your choice: ::: [`bon`]: https://github.com/elastio/bon - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/guide/alternatives.md b/website/guide/alternatives.md index 612f2c96..a48e6df1 100644 --- a/website/guide/alternatives.md +++ b/website/guide/alternatives.md @@ -98,8 +98,8 @@ Another difference is that fields of collection types are considered required by [map]: https://docs.rs/bon/latest/bon/macro.map.html [set]: https://docs.rs/bon/latest/bon/macro.set.html [mutators]: https://docs.rs/typed-builder/latest/typed_builder/derive.TypedBuilder.html#mutators -[bon-on]: ../reference/builder#on -[bon-into]: ../reference/builder#into +[bon-on]: ../reference/builder/top-level/on +[bon-into]: ../reference/builder/member/into [bs-into]: https://docs.rs/buildstructor/latest/buildstructor/#into-field [db-into]: https://docs.rs/derive_builder/latest/derive_builder/#generic-setters [r1]: #special-setter-methods-for-collections diff --git a/website/guide/compatibility.md b/website/guide/compatibility.md index 68daffbe..6f47b26a 100644 --- a/website/guide/compatibility.md +++ b/website/guide/compatibility.md @@ -2,7 +2,7 @@ ## Making a required member optional -It's totally backwards compatible to make a required member optional by changing the type from `T` to `Option` or by adding [`#[builder(default)]`](../reference/builder.md#default) to it. +It's totally backwards compatible to make a required member optional by changing the type from `T` to `Option` or by adding [`#[builder(default)]`](../reference/builder/member/default) to it. This is because both required and optional members have a setter that accepts `T` (not wrapped in an `Option`). The only change to the public API when making the required member optional is that a `maybe_`-prefixed setter is added to the builder. That new method accepts an `Option`. @@ -175,33 +175,8 @@ let user = User::builder() .build(); ``` -## Adding `#[builder]` to existing code +## Adding `#[builder]` to existing functions -If your existing code defines functions with positional parameters in its public API that you'd like to change to use builder syntax, but you want to keep the old code compatible with the positional functions API, then you may use `#[builder(expose_positional_fn)]` attribute to keep both syntaxes available. +Let's suppose you have existing code that defines functions with positional parameters in the public API. You'd like to change it to expose builder syntax instead, but you want to keep the old code compatible with the positional functions API. -See [this attribute's docs](../reference/builder#expose-positional-fn) for details. - -**Example:** - -```rust ignore -use bon::builder; - -// The previous name of the positional function needs to be specified -// as the value for `expose_positional_fn`. -#[builder(expose_positional_fn = example)] // [!code ++] -fn example(x: u32, y: u32) {} // [!code --] -fn example_builder(x: u32, y: u32) {} // [!code ++] - -// The positional function is now available under the specified (old) name -example(1, 2); - -// The builder syntax is also available under the new function name -example_builder() - .x(1) - .y(2) - .call(); -``` - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments +In this case, you may use the top-level attribute `#[builder(start_fn)]` to keep both syntaxes available. See examples in the [docs for this attribute](../reference/builder/top-level/start-fn#exposing-original-function). diff --git a/website/guide/inspecting.md b/website/guide/inspecting.md index 063d7ab0..1872c70b 100644 --- a/website/guide/inspecting.md +++ b/website/guide/inspecting.md @@ -1,6 +1,6 @@ # Inspecting -If you want to inspect the values set in the builder for debugging purposes you can leverage the [`#[builder(derive(...))]`](../reference/builder#derive) attribute to derive the [`Debug`](https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html) trait for your builder. +If you want to inspect the values set in the builder for debugging purposes you can leverage the [`#[builder(derive(...))]`](../reference/builder/top-level/derive) attribute to derive the [`Debug`](https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html) trait for your builder. **Example:** @@ -30,4 +30,4 @@ assert_eq!( builder.is_admin(true).call(); ``` -You can also derive the [`Clone`](https://doc.rust-lang.org/stable/std/clone/trait.Clone.html) trait for your builder using this same attribute. See more details in the [reference for the `#[builder(derive(...))]` attribute](../reference/builder#derive). +You can also derive the [`Clone`](https://doc.rust-lang.org/stable/std/clone/trait.Clone.html) trait for your builder using this same attribute. See more details in the [reference for the `#[builder(derive(...))]` attribute](../reference/builder/top-level/derive). diff --git a/website/guide/limitations.md b/website/guide/limitations.md index bb83ca2c..21b20faf 100644 --- a/website/guide/limitations.md +++ b/website/guide/limitations.md @@ -143,7 +143,3 @@ It's possible to place `#[builder]` on top of a `const fn`, but the generated bu If you have a strong use case that requires full support for `const`, feel free to [open an issue]. We'll figure something out for sure 🐱. [open an issue]: https://github.com/elastio/bon/issues - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/guide/optional-members.md b/website/guide/optional-members.md index 814512c0..3693c6e3 100644 --- a/website/guide/optional-members.md +++ b/website/guide/optional-members.md @@ -56,7 +56,7 @@ example().maybe_level(value).call(); ## `#[builder(default)]` -To make a member of non-`Option` type optional you may use the attribute [`#[builder(default)]`](../reference/builder#default). This attribute uses the [`Default`](https://doc.rust-lang.org/stable/std/default/trait.Default.html) trait or the provided expression to assign the default value for the member. +To make a member of non-`Option` type optional you may use the attribute [`#[builder(default)]`](../reference/builder/member/default). This attribute uses the [`Default`](https://doc.rust-lang.org/stable/std/default/trait.Default.html) trait or the provided expression to assign the default value for the member. ::: tip @@ -104,8 +104,3 @@ assert_eq!(result, 8); ## Conditional building Now that you know how optional members work you can check out the ["Conditional building" patterns](./patterns/conditional-building) or continue studying other features of `bon` by following the "Next page" link at the bottom. - - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/guide/overview.md b/website/guide/overview.md index 003608e4..affa9157 100644 --- a/website/guide/overview.md +++ b/website/guide/overview.md @@ -1,7 +1,11 @@ # Overview @@ -223,7 +227,7 @@ Project::builder() However, you can ask `bon` to generate setters that accept `impl Into` to remove the need for manual conversion. -This can be configured with [`#[builder(into)]`](../reference//builder#into) for a single member or with [`#[builder(on({type}, into))]`](../reference/builder#on) for many members at once. +This can be configured with [`#[builder(into)]`](../reference/builder/member/into) for a single member or with [`#[builder(on({type}, into))]`](../reference/builder/top-level/on) for many members at once. ```rust use bon::Builder; @@ -303,7 +307,3 @@ If you can't figure something out, consult the docs and maybe use that search ` This project was heavily inspired by such awesome crates as [`buildstructor`](https://docs.rs/buildstructor), [`typed-builder`](https://docs.rs/typed-builder) and [`derive_builder`](https://docs.rs/derive_builder). This crate was designed with many lessons learned from them. See [alternatives](./alternatives) for comparison. - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/guide/patterns/conditional-building.md b/website/guide/patterns/conditional-building.md index ea8f0cea..d7b373a0 100644 --- a/website/guide/patterns/conditional-building.md +++ b/website/guide/patterns/conditional-building.md @@ -138,7 +138,3 @@ if 2 + 2 == 4 { // Pass the variables to the builder now // ... ``` - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/guide/patterns/into-conversions-in-depth.md b/website/guide/patterns/into-conversions-in-depth.md index 3db8d730..27406257 100644 --- a/website/guide/patterns/into-conversions-in-depth.md +++ b/website/guide/patterns/into-conversions-in-depth.md @@ -373,7 +373,3 @@ Example::builder() This is quite subjective, but `impl Into` is a bit harder to read than just `T`. It makes the signature of the setter slightly bigger and requires you to understand what the `impl Trait` does, and what its implications are. If you want to keep your code simpler and more accessible (especially for beginner rustaceans), just avoid the `Into` conversions. - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/guide/patterns/shared-configuration.md b/website/guide/patterns/shared-configuration.md index f550bb08..1561df45 100644 --- a/website/guide/patterns/shared-configuration.md +++ b/website/guide/patterns/shared-configuration.md @@ -33,7 +33,7 @@ struct MyLovelyStruct2 { /**/ } ::: tip -This code uses the [`#[builder(on(...))]`](../../reference/builder#on) attribute to configure the types of members for which `bon` should enable `Into` conversions. +This code uses the [`#[builder(on(...))]`](../../reference/builder/top-level/on) attribute to configure the types of members for which `bon` should enable `Into` conversions. ::: diff --git a/website/guide/positional-members.md b/website/guide/positional-members.md index 95001b24..650f12fc 100644 --- a/website/guide/positional-members.md +++ b/website/guide/positional-members.md @@ -10,8 +10,8 @@ As an example, suppose we have a `Treasure` struct with `x` and `y` coordinates To do that we can use the `#[builder(start_fn)]` attribute. There are two contexts where we can place it, and they both have a different meaning: -- [Top-level `#[builder(start_fn = ...)]`](../reference/builder#start-fn) - configures the name of the starting function and optionally its visibility -- [Member-level `#[builder(start_fn)]`](../reference/builder#start-fn-1) - configures the member to be a positional parameter on the starting function +- [Top-level `#[builder(start_fn = ...)]`](../reference/builder/top-level/start-fn) - configures the name of the starting function and optionally its visibility +- [Member-level `#[builder(start_fn)]`](../reference/builder/member/start-fn) - configures the member to be a positional parameter on the starting function We'll want to use both of these attributes in our example to give a better name for the starting function that describes its inputs and configure `x` and `y` as positional parameters on the starting function as well. @@ -56,7 +56,7 @@ impl Treasure { Now let's say we need to know the person who claimed the `Treasure`. While describing the treasure using the current builder syntax we'd like the person who claimed it to specify their first name and last name at the end of the building process. -We can use a similar combination of the [top-level `#[builder(finish_fn = ...)]`](../reference/builder#finish-fn) and the [member-level `#[builder(finish_fn)]`](../reference/builder#finish-fn-1) attributes to do that. +We can use a similar combination of the [top-level `#[builder(finish_fn = ...)]`](../reference/builder/top-level/finish-fn) and the [member-level `#[builder(finish_fn)]`](../reference/builder/member/finish-fn) attributes to do that. **Example:** @@ -97,7 +97,7 @@ assert_eq!(treasure.claimed_by_last_name, "Heartstrings"); // [!code highlight] ## Into conversions -You may also combine these attributes with [`#[builder(into)]`](../reference/builder#into) or [`#[builder(on(..., into))]`](../reference/builder#into) to reduce the number of `to_owned()` calls a bit. +You may also combine these attributes with [`#[builder(into)]`](../reference/builder/member/into) or [`#[builder(on(..., into))]`](../reference/builder/top-level/on) to reduce the number of `to_owned()` calls a bit. ```rust use bon::Builder; diff --git a/website/reference/builder.md b/website/reference/builder.md index e5ff9ba3..2974d47a 100644 --- a/website/reference/builder.md +++ b/website/reference/builder.md @@ -51,1263 +51,29 @@ impl Example { --- -Most of the attributes apply to all kinds of syntax. However, some of them are only available with structs or only with functions/methods, for example. The **"Applies to"** clause specifies the contexts where the attribute can be used. - -::: tip Historical note - -`#[derive(bon::Builder)]` syntax appeared with the version `2.2` of `bon`. The older versions of `bon` (i.e. `<= 2.1`) supported only `#[bon::builder]` syntax with structs, but that syntax was deprecated in favor of the `derive` syntax for various reasons described in the [2.2 release blog post](../blog/bon-builder-v2-2-release). - -::: +Most of the attributes apply both to `struct` and `fn` syntaxes, but there are exceptions. The **"Applies to"** clause at the top of the detailed docs clarifies this for every attribute. ## Top-level attributes -### `builder_type` - -**Applies to:** - -Overrides the name of the generated builder struct. - -The default naming pattern is the following: - -| Underlying item | Naming pattern | -| -------------------------- | --------------------------------------------- | -| Struct | `{StructName}Builder` | -| `StructName::new()` method | `{StructName}Builder` | -| Free function | `{PascalCaseFunctionName}Builder` | -| Associated method | `{SelfTypeName}{PascalCaseMethodName}Builder` | - -The attribute expects the desired builder type identifier as its input. - -**Example:** - -::: code-group - -```rust [Struct] -use bon::Builder; - -#[derive(Builder)] -#[builder(builder_type = MyBuilder)] // [!code highlight] -struct Brush {} - -let builder: MyBuilder = Brush::builder(); -``` - -```rust [Free function] -use bon::builder; - -#[builder(builder_type = MyBuilder)] // [!code highlight] -fn brush() {} - -let builder: MyBuilder = brush(); -``` - -```rust [Associated method] -use bon::bon; - -struct Brush; - -#[bon] -impl Brush { - #[builder(builder_type = MyBuilder)] // [!code highlight] - fn new() -> Self { - Self - } -} - -let builder: MyBuilder = Brush::builder(); -``` - -::: - -You'll usually want to override the builder type name when you already have such a name in scope. For example, if you have a struct and a function with the same name annotated with `#[builder]`: - -::: code-group - -```rust compile_fail [Errored] -use bon::{builder, Builder}; - -#[derive(Builder)] // [!code error] -struct Brush {} - -#[builder] // [!code error] -fn brush() {} - -// `BrushBuilder` builder type name was generated for both -// the struct and the function. This is a compile error -let builder: BrushBuilder = Brush::builder(); -let builder: BrushBuilder = brush(); -``` - -```rust [Fixed] -use bon::{builder, Builder}; - -#[derive(Builder)] -#[builder(builder_type = MyBuilder)] // [!code highlight] -struct Brush {} - -#[builder] -fn brush() {} - -// Now builder types are named differently -let builder: MyBuilder = Brush::builder(); -let builder: BrushBuilder = brush(); -``` - -::: - -### `derive` - -**Applies to:** - -Generates additional derives on the builder type. The syntax is similar to the regular `#[derive(...)]` attribute. You need to specify one or more of the supported derives separated by commas. - -The following derives are supported: `Clone`, `Debug`. - -::: warning -The format of the `Debug` output of the builder is not stable and it may change between the patch versions of `bon`. -::: - -**Example:** - -::: code-group - -```rust [Struct] -use bon::Builder; - -#[derive(Builder)] -#[builder(derive(Clone, Debug))] // [!code highlight] -struct Example { - name: String, - is_admin: bool, - level: Option, -} - -let builder = Example::builder().name("Bon".to_owned()); - -// We can clone the builder // [!code highlight] -let builder = builder.clone(); // [!code highlight] - -// We can debug-format the builder // [!code highlight] -let builder_debug = format!("{builder:?}"); // [!code highlight] - -assert_eq!( - builder_debug, - // Only the fields that were set will be output - r#"ExampleBuilder { name: "Bon" }"# -); - -// Finish building -let example = builder.is_admin(true).build(); -``` - -```rust [Free function] -use bon::builder; - -#[builder(derive(Clone, Debug))] // [!code highlight] -fn example( - name: String, - is_admin: bool, - level: Option, -) {} - -let builder = example().name("Bon".to_owned()); - -// We can clone the builder // [!code highlight] -let builder = builder.clone(); // [!code highlight] - -// We can debug-format the builder // [!code highlight] -let builder_debug = format!("{builder:?}"); // [!code highlight] - -assert_eq!( - builder_debug, - // Only the fields that were set will be output - r#"ExampleBuilder { name: "Bon" }"# -); - -// Finish building -builder.is_admin(true).call(); -``` - -```rust [Associated method] -use bon::bon; - -#[derive(Debug)] -struct Example; - -#[bon] -impl Example { - #[builder(derive(Clone, Debug))] // [!code highlight] - fn method( - name: String, - is_admin: bool, - level: Option, - ) {} - - #[builder(derive(Debug))] - fn method_with_self(&self) {} -} - -let builder = Example::method().name("Bon".to_owned()); - -// We can clone the builder // [!code highlight] -let builder = builder.clone(); // [!code highlight] - -// We can debug-format the builder // [!code highlight] -let builder_debug = format!("{builder:?}"); // [!code highlight] - -assert_eq!( - builder_debug, - // Only the fields that were set will be output - r#"ExampleMethodBuilder { name: "Bon" }"# -); - -// Finish building -builder.is_admin(true).call(); - -// The debug output of the builder for methods with `self` includes -// the special `self` field with the receiver. -assert_eq!( - format!("{:?}", Example.method_with_self()), - "ExampleMethodWithSelfBuilder { self: Example }" -) -``` - -::: - -#### Compile errors - -_Requires_ that all members of the builder including the receiver (if this is a builder for an associated method) implement the target trait. For example, this doesn't compile because not all members implement `Clone`: - -**Example:** - -```rust compile_fail -use bon::Builder; - -struct NonClone; - -#[derive(Builder)] -#[builder(Clone)] -struct Example { - // Doesn't derive `Clone`, so this code doesn't compile // [!code error] - non_clone NonClone, // [!code error] - cloneable: u32 -} -``` - -### `expose_positional_fn` - -**Applies to:** - -When generating builder code for functions the `#[builder]` macro hides the original function with positional parameters used to define the builder. That function is invoked inside of the builder's `call()` or `build()` method. - -Usually you'd want the underlying positional function to be hidden to provide only the builder syntax to the callers. However, in some situations you may want to keep the positional function exposed along with the builder syntax for compatibility with old code that still uses the old positional function call syntax. - -This attribute can take several forms. - -- Simple: `#[builder(expose_positional_fn = identifier)]`. Sets only the name of the positional function. -- Verbose: `#[builder(expose_positional_fn(name = identifier, vis = "visibility"))]`. - Allows setting both the name and the visibility of the positional function. - Each key is optional. The `vis` must be specified as a string literal e.g. `"pub(crate)"`, `"pub"` or `""` (empty string means private visibility). - -If `vis` parameter is not specified, then the visibility of the exposed positional function will be the same as specified on the function that the `#[builder]` was applied to. - -**Example:** - -::: code-group - -```rust ignore [Free function] -use bon::builder; - -#[builder(expose_positional_fn = example_positional)] // [!code highlight] -fn example(x: u32, y: u32) {} - -// Positional function is now available under the given name // [!code highlight] -example_positional(1, 2); // [!code highlight] - -// Builder syntax is also available (unchanged) -example() - .x(1) - .y(2) - .call(); -``` - -```rust ignore [Associated method] -use bon::bon; - -struct Example; - -#[bon] -impl Example { - #[builder(expose_positional_fn = example_positional)] // [!code highlight] - fn example(x: u32, y: u32) {} -} - -// Positional function is now available under the given name // [!code highlight] -Example::example_positional(1, 2); // [!code highlight] - -// Builder syntax is also available (unchanged) -Example::example() - .x(1) - .y(2) - .call(); -``` - -::: - -#### `new` method special case - -There are two conventional names in Rust ecosystem for constructors and builders: - -- `new` is used for a constructor method that uses positional parameters -- `builder` is used for a method that returns a builder for a type - -So when `#[builder]` is placed on a method called `new`, it'll generate a method called `builder` that starts the building process. This means there is already a default obvious name for the positional function that `expose_positional_fn` may use in this case if you don't specify any value for this attribute. - -**Example:** - -```rust ignore -use bon::bon; - -struct Example { - x: u32, - y: u32, -} - -#[bon] -impl Example { - #[builder(expose_positional_fn)] // [!code highlight] - fn new(x: u32, y: u32) -> Self { - Self { x, y } - } -} - -// Positional function is available under the name `new` // [!code highlight] -Example::new(1, 2); // [!code highlight] - -// Builder syntax is also available (unchanged) -Example::builder() - .x(1) - .y(2) - .build(); -``` - -This makes it possible to add builder syntax to your existing types that have the `new` method without breaking compatibility with old code. Old code can still use `T::new()` syntax, while new code can benefit from `T::builder()` syntax. - -### `finish_fn` - -**Applies to:** - -This attribute allows overriding the name of the generated builder's method that finishes the building process. - -**Example:** - -::: code-group - -```rust [Struct] -use bon::Builder; - -#[derive(Builder)] -#[builder(finish_fn = assemble)] // [!code highlight] -struct Article { - id: u32 -} - -let article = Article::builder() - .id(42) - .assemble(); // [!code highlight] - -assert_eq!(article.id, 42); -``` - -```rust [Free function] -use bon::builder; - -#[builder(finish_fn = send)] // [!code highlight] -fn get_article(id: u32) -> String { - format!("Some article with id {id}") -} - -let response = get_article() - .id(42) - .send(); // [!code highlight] - -assert_eq!(response, "Some article with id 42"); -``` - -```rust [Associated method] -use bon::bon; - -struct ArticlesClient; - -#[bon] -impl ArticlesClient { - #[builder(finish_fn = send)] // [!code highlight] - fn get_article(&self, id: u32) -> String { - format!("Some article with id {id}") - } -} - -let response = ArticlesClient - .get_article() - .id(42) - .send(); // [!code highlight] - -assert_eq!(response, "Some article with id 42"); -``` - -::: - -### `on` - -**Applies to:** - -Applies the given builder attributes to all members that match the selected type pattern. For example, you can automatically apply `#[builder(into)]` to all members of type `String` this way: - -::: code-group - -```rust [Struct] -use bon::Builder; - -#[derive(Builder)] -#[builder(on(String, into))] -struct Example { - id: String, - name: String, - level: u32, -} - -Example::builder() - // `id` and `name` accept `impl Into` because // [!code highlight] - // `on` automatically added `#[builder(into)]` for them // [!code highlight] - .id("e-1111") - .name("Bon") - // `u32` doesn't match the `String` type pattern, // [!code highlight] - // so `#[builder(into)]` was not applied to it // [!code highlight] - .level(100) - .build(); -``` - -```rust [Free function] -use bon::builder; - -#[builder(on(String, into))] -fn example( - id: String, - name: String, - level: u32, -) {} - -example() - // `id` and `name` accept `impl Into` because // [!code highlight] - // `on` automatically added `#[builder(into)]` for them // [!code highlight] - .id("e-1111") - .name("Bon") - // `u32` doesn't match the `String` type pattern, // [!code highlight] - // so `#[builder(into)]` was not applied to it // [!code highlight] - .level(100) - .call(); -``` - -```rust [Associated method] -use bon::bon; - -struct Example; - -#[bon] -impl Example { - #[builder(on(String, into))] - fn example( - id: String, - name: String, - level: u32, - ) {} -} - -Example::example() - // `id` and `name` accept `impl Into` because // [!code highlight] - // `on` automatically added `#[builder(into)]` for them // [!code highlight] - .id("e-1111") - .name("Bon") - // `u32` doesn't match the `String` type pattern, // [!code highlight] - // so `#[builder(into)]` was not applied to it // [!code highlight] - .level(100) - .call(); -``` - -::: - -This attribute must be of form `on(type_pattern, attributes)`. - -- `type_pattern` - type that will be compared with the types of the members. The types are compared textually. For example, `String` doesn't match `std::string::String`. You can use `_` to mark parts of the type to ignore when matching. For example, `Vec<_>` matches `Vec` or `Vec`. Lifetimes are ignored during matching. - -- `attributes` - for now, the only attribute supported in the `attributes` position is [`into`](#into). It sets `#[builder(into)]` for members that match the `type_pattern`. - -If you want to apply the `attributes` to all members, you can use the `_` type pattern that matches any type. For example, `#[builder(on(_, into))]`. - -For optional members the underlying type is matched ignoring the `Option` wrapper. - -**Example:** +Click on the name of the attribute to view detailed docs. -```rust -use bon::Builder; - -#[derive(Builder)] -#[builder(on(String, into))] -struct Example { - name: String, - description: Option, - - #[builder(default)] - alias: String -} - -Example::builder() - .name("Bon") - // These members also match the `String` type pattern, - // so `#[builder(into)]` was applied to them - .description("accepts an `impl Into` here") - .alias("builder") - .build(); -``` - -You can specify `on(...)` multiple times. - -**Example:** - -```rust -use bon::Builder; -use std::path::PathBuf; - -#[derive(Builder)] -#[builder(on(String, into), on(PathBuf, into))] -struct Example { - name: String, - path: PathBuf, - level: u32, -} - -Example::builder() - .name("accepts `impl Into`") - .path("accepts/impl/into/PathBuf") - // This member doesn't match either `String` or `PathBuf`, - // and thus #[builder(into)] was not applied to it - .level(100) - .build(); -``` - -### `start_fn` - -**Applies to:** - -Overrides the name and visibility of the associated method that starts the building process, i.e. returns the builder for the struct. - -The default name for this method is `builder`, and the default visibility is the same as the visibility of the struct itself. - -This attribute can take several forms. - -- Simple: `#[builder(start_fn = identifier)]`. Overrides only the name of the "start" method. -- Verbose: `#[builder(start_fn(name = identifier, vis = "visibility"))]`. - Allows overriding both the name and the visibility of the "start" method. - Each key is optional. The `vis` must be specified as a string literal e.g. `"pub(crate)"`, `"pub"` or `""` (empty string means private visibility). - -**Example:** - -::: code-group - -```rust [Simple form] -use bon::Builder; - -#[derive(Builder)] -#[builder(start_fn = init)] // [!code highlight] -struct User { - id: u32 -} - -User::init() // [!code highlight] - .id(42) - .build(); -``` - -```rust [Verbose form] -use bon::Builder; - -// `User::init()` method will have `pub(crate)` visibility // [!code highlight] -// Use `vis = ""` to make it fully private instead // [!code highlight] -#[derive(Builder)] -#[builder(start_fn(name = init, vis = "pub(crate)"))] // [!code highlight] -pub struct User { - id: u32 -} - -User::init() // [!code highlight] - .id(42) - .build(); -``` - -::: +| Attribute | Short description +| -- | -- | +| [`builder_type`](./builder/top-level/builder-type) | Overrides the name, visibility and docs of the generated builder +| [`derive`](./builder/top-level/derive) | Generates additional derives on the builder struct itself +| [`finish_fn`](./builder/top-level/finish-fn) | Overrides the name, visibility and docs of the finishing function +| [`on`](./builder/top-level/on) | Applies the given builder attributes to all members matching a type pattern +| [`start_fn`](./builder/top-level/start-fn) | Overrides the name, visibility and docs of the starting function ## Member attributes -### `default` - -**Applies to:** - -Makes the member optional and assigns a default value to it. There will be two setter methods generated for the member just like for [members of type `Option`](../guide/optional-members). One setter accepts a value of type `T` (type of the member) and the other (with the `maybe_` prefix) accepts an `Option`. - -::: tip - -Switching between `#[builder(default)]` and `Option` is [compatible](../guide/compatibility#switching-between-option-t-and-builder-default). - -::: - -The default value will be lazily computed inside of the [finishing function](#finish-fn) (i.e. `build()` or `call()`). It is computed only if the setter for the member wasn't called or `None` was passed to the `maybe_{member}()` setter. - -The default value is computed based on the form of this attribute: - -| Form | How default value is computed | -| ---------------------------------- | ----------------------------- | -| `#[builder(default)]` | `Default::default()` | -| `#[builder(default = expression)]` | `expression` | - -The result of the `expression` will be converted into the target type using [`Into::into`](https://doc.rust-lang.org/stable/std/convert/trait.Into.html) if [`#[builder(into)]`](#into) is enabled for the setter. - -**Example:** - -::: code-group - -```rust [Struct field] -use bon::Builder; - -#[derive(Builder)] -struct User { - #[builder(default)] // [!code highlight] - level: u32, - - // The expression of type `&'static str` is automatically // [!code highlight] - // converted to `String` here via `Into` thanks to `#[builder(into)]. // [!code highlight] - #[builder(into, default = "anon")] // [!code highlight] - name: String, - - // Any complex expression is accepted // [!code highlight] - #[builder(default = bon::vec!["read"])] // [!code highlight] - permissions: Vec, -} - -let user = User::builder().build(); - -assert_eq!(user.level, 0); -assert_eq!(user.name, "anon"); -assert_eq!(user.permissions, ["read"]); -``` - -```rust [Free function argument] -use bon::builder; - -#[builder] -fn greet_user( - #[builder(default)] // [!code highlight] - level: u32, - - // The expression of type `&'static str` is automatically // [!code highlight] - // converted to `String` here via `Into` thanks to `#[builder(into)]. // [!code highlight] - #[builder(into, default = "anon")] // [!code highlight] - name: String, - - // Any complex expression is accepted // [!code highlight] - #[builder(default = bon::vec!["read"])] // [!code highlight] - permissions: Vec, -) -> String { - format!("Hello {name}! Your level is {level}, permissions: {permissions:?}") -} - -let greeting = greet_user().call(); - -assert_eq!(greeting, "Hello anon! Your level is 0, permissions: [\"read\"]"); -``` - -```rust [Associated method argument] -use bon::bon; - -struct User { - level: u32, - name: String, - permissions: Vec, -} - -#[bon] -impl User { - #[builder] - fn new( - #[builder(default)] // [!code highlight] - level: u32, - - // The expression of type `&'static str` is automatically // [!code highlight] - // converted to `String` here via `Into` thanks to `#[builder(into)]. // [!code highlight] - #[builder(into, default = "anon")] // [!code highlight] - name: String, - - // Any complex expression is accepted // [!code highlight] - #[builder(default = bon::vec!["read"])] // [!code highlight] - permissions: Vec, - ) -> Self { - Self { level, name, permissions } - } -} - -let user = User::builder().build(); - -assert_eq!(user.name, "anon"); -assert_eq!(user.level, 0); -assert_eq!(user.permissions, ["read"]); -``` - -::: - -You can also use the values of other members by referencing their names in the `default` expression. All members are initialized in the order of their declaration. It means only those members that are declared earlier (higher) in the code are available to the `default` expression. - -**Example:** - -::: code-group - -```rust [Struct field] -use bon::Builder; - -#[derive(Builder)] -struct Example { - member_1: u32, - - // Note that here we don't have access to `member_3` - // because it's declared (and thus initialized) later - #[builder(default = 2 * member_1)] - member_2: u32, - - #[builder(default = member_2 + member_1)] - member_3: u32, -} - -let example = Example::builder() - .member_1(3) - .build(); - -assert_eq!(example.member_1, 3); -assert_eq!(example.member_2, 6); -assert_eq!(example.member_3, 9); -``` - -```rust [Free function argument] -use bon::builder; - -#[builder] -fn example( - member_1: u32, - - // Note that here we don't have access to `member_3` - // because it's declared (and thus initialized) later - #[builder(default = 2 * member_1)] - member_2: u32, - - #[builder(default = member_2 + member_1)] - member_3: u32, -) -> (u32, u32, u32) { - (member_1, member_2, member_3) -} - -let example = example() - .member_1(3) - .call(); - -assert_eq!(example, (3, 6, 9)); -``` - -```rust [Associated method argument] -use bon::bon; - -struct Example; - -#[bon] -impl Example { - #[builder] - fn example( - member_1: u32, - - // Note that here we don't have access to `member_3` - // because it's declared (and thus initialized) later - #[builder(default = 2 * member_1)] - member_2: u32, - - #[builder(default = member_2 + member_1)] - member_3: u32, - ) -> (u32, u32, u32) { - (member_1, member_2, member_3) - } -} - -let example = Example::example() - .member_1(3) - .call(); - -assert_eq!(example, (3, 6, 9)); -``` - -::: - -#### Caveats - -The `self` parameter in associated methods is not available to the `default` expression. If you need the `self` context for your defaulting logic, then set your member's type to `Option` and handle the defaulting in the function's body manually. - -#### Compile errors - -This attribute is incompatible with members of `Option` type, since `Option` already implies the default value of `None`. - -### `finish_fn` - -**Applies to:** - -Makes the member a positional argument on the finishing function that consumes the builder and returns the resulting object (for struct syntax) or performs the requested action (for function/method syntax). - -The ordering of members annotated with `#[builder(finish_fn)]` matters! They will appear in the same order relative to each other in the finishing function signature. They must also be declared at the top of the members list strictly after members annotated with [`#[builder(start_fn)]`](#start-fn-1) (if any). - -This ensures a consistent initialization order, and it makes these members available for expressions in `#[builder(default/skip = ...)]` for regular members that follow them. - -::: tip - -Don't confuse this with the top-level [`#[builder(finish_fn = ...)]`](#finish-fn) attribute that can be used to configure the name and visibility of the finishing function. You'll likely want to use it in combination with this member-level attribute to define a better name for the finishing function. - -::: - -**Example:** - -::: code-group - -```rust [Struct field] -use bon::Builder; - -#[derive(Builder)] -// Top-level attribute to give a better name for the finishing function // [!code highlight] -#[builder(finish_fn = sign)] // [!code highlight] -struct Message { - // Member-level attribute to mark the member as a parameter of `sign()` // [!code highlight] - #[builder(finish_fn)] // [!code highlight] - author_first_name: String, - - #[builder(finish_fn)] // [!code highlight] - author_last_name: String, - - payload: String, -} - -let message = Message::builder() - .payload("Bon is great! Give it a ⭐".to_owned()) - .sign("Sweetie".to_owned(), "Drops".to_owned()); - -assert_eq!(message.payload, "Bon is great! Give it a ⭐"); -assert_eq!(message.author_first_name, "Sweetie"); -assert_eq!(message.author_last_name, "Drops"); -``` - -```rust [Free function] -use bon::builder; - -// Top-level attribute to give a better name for the finishing function // [!code highlight] -#[builder(finish_fn = send)] // [!code highlight] -fn message( - // Member-level attribute to mark the member as a parameter of `sign()` // [!code highlight] - #[builder(finish_fn)] // [!code highlight] - receiver_first_name: String, - - #[builder(finish_fn)] // [!code highlight] - receiver_last_name: String, - - payload: String, -) {} - -message() - .payload("Bon is great! Give it a ⭐".to_owned()) - .send("Sweetie".to_owned(), "Drops".to_owned()); -``` - -```rust [Associated method] -use bon::bon; - -struct Chat {} - -#[bon] -impl Chat { - // Top-level attribute to give a better name for the finishing function // [!code highlight] - #[builder(finish_fn = send)] // [!code highlight] - fn message( - &self, - - // Member-level attribute to mark the member as a parameter of `sign()` // [!code highlight] - #[builder(finish_fn)] // [!code highlight] - receiver_first_name: String, - - #[builder(finish_fn)] // [!code highlight] - receiver_last_name: String, - - payload: String, - ) {} -} - -let chat = Chat {}; - -chat.message() - .payload("Bon is great! Give it a ⭐".to_owned()) - .send("Sweetie".to_owned(), "Drops".to_owned()); -``` - -::: - -You can also combine this attribute with [`#[builder(into)]`](#into) or [`#[builder(on(..., into))]`](#on) to add an into conversion for the parameter. - -Importantly, `Into` conversions for such members work slightly differently from the regular (named) members in regard to the `Option` type. The `Option` type gives no additional meaning to the member annotated with `#[builder(finish_fn)]`. Thus, it is matched by the type pattern of `on(..., into)` and wrapped with `impl Into>` as any other type. - -::: tip - -In general, it's not recommended to annotate optional members with `#[builder(finish_fn)]` because you can't omit setting them using the positional function call syntax. - -::: - -### `into` - -**Applies to:** - -Changes the signature of the generated setters to accept [`impl Into`](https://doc.rust-lang.org/stable/std/convert/trait.Into.html), where `T` is the type of the member. - -For [optional members](../guide/optional-members), the `maybe_{member}()` setter method will accept an `Option>` type instead of just `Option`. - -For members that use `#[builder(default = expression)]`, the `expression` will be converted with `Into::into`. - -This parameter is often used with the `String` type, which allows you to pass `&str` into the setter without calling `.to_owned()` or `.to_string()` on it. - -See the ["Into Conversions In-Depth"](../guide/patterns/into-conversions-in-depth) page that shows the common patterns and antipatterns of `impl Into`. - -**Example:** - -::: code-group - -```rust [Struct field] -use bon::Builder; - -#[derive(Builder)] -struct Example { - #[builder(into)] // [!code highlight] - name: String, - - #[builder(into)] // [!code highlight] - description: Option, - - // The value passed to `default = ...` is converted with `into` as well // [!code highlight] - #[builder(into, default = "anon")] // [!code highlight] - group: String -} - -Example::builder() - // We can pass `&str` because the setters accept `impl Into` // [!code highlight] - .name("Bon") // [!code highlight] - .description("Awesome crate 🐱. Consider giving it a star on Github ⭐") // [!code highlight] - // We can pass `Option<&str>` to `maybe_` methods because they accept // [!code highlight] - // `Option>` // [!code highlight] - .maybe_group(Some("Favourites")) // [!code highlight] - .build(); -``` - -```rust [Free function argument] -use bon::builder; - -#[builder] -fn example( - #[builder(into)] // [!code highlight] - name: String, - - #[builder(into)] // [!code highlight] - description: Option, - - // The value passed to `default = ...` is converted with `into` as well // [!code highlight] - #[builder(into, default = "anon")] // [!code highlight] - group: String -) {} - -example() - // We can pass `&str` because the setters accept `impl Into` // [!code highlight] - .name("Bon") // [!code highlight] - .description("Awesome crate 🐱. Consider giving it a star on Github ⭐") // [!code highlight] - // We can pass `Option<&str>` to `maybe_` methods because they accept // [!code highlight] - // `Option>` // [!code highlight] - .maybe_group(Some("Favourites")) // [!code highlight] - .call(); -``` - -```rust [Associated method argument] -use bon::bon; - -struct Example; - -#[bon] -impl Example { - #[builder] - fn example( - #[builder(into)] // [!code highlight] - name: String, - - #[builder(into)] // [!code highlight] - description: Option, - - // The value passed to `default = ...` is converted with `into` as well // [!code highlight] - #[builder(into, default = "anon")] // [!code highlight] - group: String - ) {} -} - -Example::example() - // We can pass `&str` because the setters accept `impl Into` // [!code highlight] - .name("Bon") // [!code highlight] - .description("Awesome crate 🐱. Consider giving it a star on Github ⭐") // [!code highlight] - // We can pass `Option<&str>` to `maybe_` methods because they accept // [!code highlight] - // `Option>` // [!code highlight] - .maybe_group(Some("Favourites")) // [!code highlight] - .call(); -``` - -::: - -### `name` - -**Applies to:** - -Overrides the name of the member in the builder's setters and type state. This is most useful when with struct syntax (`#[derive(Builder)]`) where you'd like to use a different name for the field internally. For functions this attribute makes less sense since it's easy to just create a variable named differently `let new_name = param_name;`. However, this attribute is still supported on function arguments. - -**Example:** - -::: code-group - -```rust [Struct field] -use bon::Builder; - -#[derive(Builder)] -struct Player { - #[builder(name = rank)] // [!code highlight] - level: u32 -} - -Player::builder() - .rank(10) // [!code highlight] - .build(); -``` - -```rust [Free function argument] -use bon::builder; - -#[builder] -fn player( - #[builder(name = rank)] // [!code highlight] - level: u32 -) {} - -player() - .rank(10) // [!code highlight] - .call(); -``` - -```rust [Associated method argument] -use bon::bon; - -struct Player { - level: u32, -} - -#[bon] -impl Player { - #[builder] - fn new( - #[builder(name = rank)] // [!code highlight] - level: u32 - ) -> Self { - Self { level } - } -} - -Player::builder() - .rank(10) // [!code highlight] - .build(); -``` - -::: - -### `skip` - -**Applies to:** - -Skips generating setters for the member. This hides the member from the generated builder API, so the caller can't set its value. - -The value for the member will be computed based on the form of the attribute: - -| Form | How value for the member is computed | -| ------------------------------- | ------------------------------------ | -| `#[builder(skip)]` | `Default::default()` | -| `#[builder(skip = expression)]` | `expression` | - -**Example:** - -```rust -use bon::Builder; - -#[derive(Builder)] -struct User { - #[builder(skip)] // [!code highlight] - level: u32, - - // Any complex expression is accepted // [!code highlight] - #[builder(skip = "anon".to_owned())] // [!code highlight] - name: String, -} - -let user = User::builder() - // There are no `level`, and `name` setters generated // [!code highlight] - .build(); - -assert_eq!(user.level, 0); -assert_eq!(user.name, "anon"); -``` - -You can also use the values of other members by referencing their names in the `skip` expression. All members are initialized in the order of their declaration. It means only those members that are declared earlier (higher) in the code are available to the `skip` expression. - -**Example:** - -```rust -use bon::Builder; - -#[derive(Builder)] -struct Example { - member_1: u32, - - // Note that here we don't have access to `member_3` - // because it's declared (and thus initialized) later - #[builder(skip = 2 * member_1)] - member_2: u32, - - #[builder(skip = member_2 + member_1)] - member_3: u32, -} - -let example = Example::builder() - .member_1(3) - .build(); - -assert_eq!(example.member_1, 3); -assert_eq!(example.member_2, 6); -assert_eq!(example.member_3, 9); -``` - -This attribute is not supported with free function arguments or associated method arguments because it's simply unnecessary there and can easier be expressed with local variables. - -### `start_fn` - -**Applies to:** - -Makes the member a positional argument on the starting function that creates the builder. - -The ordering of members annotated with `#[builder(start_fn)]` matters! They will appear in the same order relative to each other in the starting function signature. They must also be declared at the top of the members' list. - -This ensures a consistent initialization order, and it makes these members available for expressions in `#[builder(default/skip = ...)]` for regular members that follow them. - -::: tip - -Don't confuse this with the top-level [`#[builder(start_fn = ...)]`](#start-fn) attribute that can be used to configure the name and visibility of the starting function. You'll likely want to use it in combination with this member-level attribute to define a better name for the starting function. - -::: - -**Example:** - -::: code-group - -```rust [Struct field] -use bon::Builder; - -#[derive(Builder)] -// Top-level attribute to give a better name for the starting function // [!code highlight] -#[builder(start_fn = with_coordinates)] // [!code highlight] -struct Treasure { - // Member-level attribute to mark the member as // [!code highlight] - // a parameter of `with_coordinates()` // [!code highlight] - #[builder(start_fn)] // [!code highlight] - x: u32, - - #[builder(start_fn)] // [!code highlight] - y: u32, - - label: Option, -} - -let treasure = Treasure::with_coordinates(2, 9) // [!code highlight] - .label("knowledge".to_owned()) - .build(); - -assert_eq!(treasure.x, 2); -assert_eq!(treasure.y, 9); -assert_eq!(treasure.label.as_deref(), Some("knowledge")); -``` - -```rust [Free function] -use bon::builder; - -#[builder] -fn mark_treasure_at( - #[builder(start_fn)] // [!code highlight] - x: u32, - - #[builder(start_fn)] // [!code highlight] - y: u32, - - label: Option, -) {} - -mark_treasure_at(2, 9) - .label("knowledge".to_owned()) - .call(); -``` - -```rust [Associated method] -use bon::bon; - -struct Navigator {} - -#[bon] -impl Navigator { - #[builder] - fn mark_treasure_at( - &self, - - #[builder(start_fn)] // [!code highlight] - x: u32, - - #[builder(start_fn)] // [!code highlight] - y: u32, - - label: String, - ) {} -} - -let navigator = Navigator {}; - -navigator - .mark_treasure_at(2, 9) - .label("knowledge".to_owned()) - .call(); -``` - -::: - -You can also combine this attribute with [`#[builder(into)]`](#into) or [`#[builder(on(..., into))]`](#on) to add an into conversion for the parameter. - -Importantly, `Into` conversions for such members work slightly differently from the regular (named) members in regard to the `Option` type. The `Option` type gives no additional meaning to the member annotated with `#[builder(start_fn)]`. Thus, it is matched by the type pattern of `on(..., into)` and wrapped with `impl Into>` as any other type. - -::: tip - -In general, it's not recommended to annotate optional members with `#[builder(start_fn)]` because you can't omit setting them using the positional function call syntax. - -::: +Click on the name of the attribute to view detailed docs. -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments +| Attribute | Short description +| -- | -- | +| [`default`](./builder/member/default) | Makes the member optional with a default value +| [`finish_fn`](./builder/member/finish-fn) | Makes the member a positional argument on the finishing function +| [`into`](./builder/member/into) | Changes the signature of the generated setters to accept `impl Into` +| [`name`](./builder/member/name) | Overrides the name of the member used in the builder's API +| [`skip`](./builder/member/skip) | Skips generating setters for the member +| [`start_fn`](./builder/member/start-fn) | Makes the member a positional argument on the starting function diff --git a/website/reference/builder/member/default.md b/website/reference/builder/member/default.md new file mode 100644 index 00000000..ffd1c7e0 --- /dev/null +++ b/website/reference/builder/member/default.md @@ -0,0 +1,210 @@ +# `default` + +**Applies to:** + +Makes the member optional and assigns a default value to it. There will be two setter methods generated for the member just like for [members of type `Option`](../../../guide/optional-members). One setter accepts a value of type `T` (type of the member) and the other (with the `maybe_` prefix) accepts an `Option`. + +::: tip + +Switching between `#[builder(default)]` and `Option` is [compatible](../../../guide/compatibility#switching-between-option-t-and-builder-default). + +::: + +The default value will be lazily computed inside of the [finishing function](./finish-fn) (i.e. `build()` or `call()`). It is computed only if the setter for the member wasn't called or `None` was passed to the `maybe_{member}()` setter. + +The default value is computed based on the form of this attribute: + +| Form | How default value is computed | +| ---------------------------------- | ----------------------------- | +| `#[builder(default)]` | `Default::default()` | +| `#[builder(default = expression)]` | `expression` | + +The result of the `expression` will be converted into the target type using [`Into::into`](https://doc.rust-lang.org/stable/std/convert/trait.Into.html) if [`#[builder(into)]`](./into) is enabled for the setter. + +**Example:** + +::: code-group + +```rust [Struct field] +use bon::Builder; + +#[derive(Builder)] +struct User { + #[builder(default)] // [!code highlight] + level: u32, + + // The expression of type `&'static str` is automatically // [!code highlight] + // converted to `String` here via `Into` thanks to `#[builder(into)]. // [!code highlight] + #[builder(into, default = "anon")] // [!code highlight] + name: String, + + // Any complex expression is accepted // [!code highlight] + #[builder(default = bon::vec!["read"])] // [!code highlight] + permissions: Vec, +} + +let user = User::builder().build(); + +assert_eq!(user.level, 0); +assert_eq!(user.name, "anon"); +assert_eq!(user.permissions, ["read"]); +``` + +```rust [Free function argument] +use bon::builder; + +#[builder] +fn greet_user( + #[builder(default)] // [!code highlight] + level: u32, + + // The expression of type `&'static str` is automatically // [!code highlight] + // converted to `String` here via `Into` thanks to `#[builder(into)]. // [!code highlight] + #[builder(into, default = "anon")] // [!code highlight] + name: String, + + // Any complex expression is accepted // [!code highlight] + #[builder(default = bon::vec!["read"])] // [!code highlight] + permissions: Vec, +) -> String { + format!("Hello {name}! Your level is {level}, permissions: {permissions:?}") +} + +let greeting = greet_user().call(); + +assert_eq!(greeting, "Hello anon! Your level is 0, permissions: [\"read\"]"); +``` + +```rust [Associated method argument] +use bon::bon; + +struct User { + level: u32, + name: String, + permissions: Vec, +} + +#[bon] +impl User { + #[builder] + fn new( + #[builder(default)] // [!code highlight] + level: u32, + + // The expression of type `&'static str` is automatically // [!code highlight] + // converted to `String` here via `Into` thanks to `#[builder(into)]. // [!code highlight] + #[builder(into, default = "anon")] // [!code highlight] + name: String, + + // Any complex expression is accepted // [!code highlight] + #[builder(default = bon::vec!["read"])] // [!code highlight] + permissions: Vec, + ) -> Self { + Self { level, name, permissions } + } +} + +let user = User::builder().build(); + +assert_eq!(user.name, "anon"); +assert_eq!(user.level, 0); +assert_eq!(user.permissions, ["read"]); +``` + +::: + +You can also use the values of other members by referencing their names in the `default` expression. All members are initialized in the order of their declaration. It means only those members that are declared earlier (higher) in the code are available to the `default` expression. + +**Example:** + +::: code-group + +```rust [Struct field] +use bon::Builder; + +#[derive(Builder)] +struct Example { + member_1: u32, + + // Note that here we don't have access to `member_3` + // because it's declared (and thus initialized) later + #[builder(default = 2 * member_1)] + member_2: u32, + + #[builder(default = member_2 + member_1)] + member_3: u32, +} + +let example = Example::builder() + .member_1(3) + .build(); + +assert_eq!(example.member_1, 3); +assert_eq!(example.member_2, 6); +assert_eq!(example.member_3, 9); +``` + +```rust [Free function argument] +use bon::builder; + +#[builder] +fn example( + member_1: u32, + + // Note that here we don't have access to `member_3` + // because it's declared (and thus initialized) later + #[builder(default = 2 * member_1)] + member_2: u32, + + #[builder(default = member_2 + member_1)] + member_3: u32, +) -> (u32, u32, u32) { + (member_1, member_2, member_3) +} + +let example = example() + .member_1(3) + .call(); + +assert_eq!(example, (3, 6, 9)); +``` + +```rust [Associated method argument] +use bon::bon; + +struct Example; + +#[bon] +impl Example { + #[builder] + fn example( + member_1: u32, + + // Note that here we don't have access to `member_3` + // because it's declared (and thus initialized) later + #[builder(default = 2 * member_1)] + member_2: u32, + + #[builder(default = member_2 + member_1)] + member_3: u32, + ) -> (u32, u32, u32) { + (member_1, member_2, member_3) + } +} + +let example = Example::example() + .member_1(3) + .call(); + +assert_eq!(example, (3, 6, 9)); +``` + +::: + +## Caveats + +The `self` parameter in associated methods is not available to the `default` expression. If you need the `self` context for your defaulting logic, then set your member's type to `Option` and handle the defaulting in the function's body manually. + +## Compile errors + +This attribute is incompatible with members of `Option` type, since `Option` already implies the default value of `None`. diff --git a/website/reference/builder/member/finish-fn.md b/website/reference/builder/member/finish-fn.md new file mode 100644 index 00000000..b894abd0 --- /dev/null +++ b/website/reference/builder/member/finish-fn.md @@ -0,0 +1,109 @@ + +# `finish_fn` + +**Applies to:** + +Makes the member a positional argument on the finishing function that consumes the builder and returns the resulting object (for struct syntax) or performs the requested action (for function/method syntax). + +The ordering of members annotated with `#[builder(finish_fn)]` matters! They will appear in the same order relative to each other in the finishing function signature. They must also be declared at the top of the members list strictly after members annotated with [`#[builder(start_fn)]`](./start-fn) (if any). + +This ensures a consistent initialization order, and it makes these members available for expressions in `#[builder(default/skip = ...)]` for regular members that follow them. + +::: tip + +Don't confuse this with the top-level [`#[builder(finish_fn = ...)]`](../top-level/finish-fn) attribute that can be used to configure the name and visibility of the finishing function. You'll likely want to use it in combination with this member-level attribute to define a better name for the finishing function. + +::: + +**Example:** + +::: code-group + +```rust [Struct field] +use bon::Builder; + +#[derive(Builder)] +// Top-level attribute to give a better name for the finishing function // [!code highlight] +#[builder(finish_fn = sign)] // [!code highlight] +struct Message { + // Member-level attribute to mark the member as a parameter of `sign()` // [!code highlight] + #[builder(finish_fn)] // [!code highlight] + author_first_name: String, + + #[builder(finish_fn)] // [!code highlight] + author_last_name: String, + + payload: String, +} + +let message = Message::builder() + .payload("Bon is great! Give it a ⭐".to_owned()) + .sign("Sweetie".to_owned(), "Drops".to_owned()); + +assert_eq!(message.payload, "Bon is great! Give it a ⭐"); +assert_eq!(message.author_first_name, "Sweetie"); +assert_eq!(message.author_last_name, "Drops"); +``` + +```rust [Free function] +use bon::builder; + +// Top-level attribute to give a better name for the finishing function // [!code highlight] +#[builder(finish_fn = send)] // [!code highlight] +fn message( + // Member-level attribute to mark the member as a parameter of `sign()` // [!code highlight] + #[builder(finish_fn)] // [!code highlight] + receiver_first_name: String, + + #[builder(finish_fn)] // [!code highlight] + receiver_last_name: String, + + payload: String, +) {} + +message() + .payload("Bon is great! Give it a ⭐".to_owned()) + .send("Sweetie".to_owned(), "Drops".to_owned()); +``` + +```rust [Associated method] +use bon::bon; + +struct Chat {} + +#[bon] +impl Chat { + // Top-level attribute to give a better name for the finishing function // [!code highlight] + #[builder(finish_fn = send)] // [!code highlight] + fn message( + &self, + + // Member-level attribute to mark the member as a parameter of `sign()` // [!code highlight] + #[builder(finish_fn)] // [!code highlight] + receiver_first_name: String, + + #[builder(finish_fn)] // [!code highlight] + receiver_last_name: String, + + payload: String, + ) {} +} + +let chat = Chat {}; + +chat.message() + .payload("Bon is great! Give it a ⭐".to_owned()) + .send("Sweetie".to_owned(), "Drops".to_owned()); +``` + +::: + +You can also combine this attribute with [`#[builder(into)]`](./into) or [`#[builder(on(..., into))]`](../top-level/on) to add an into conversion for the parameter. + +Importantly, `Into` conversions for such members work slightly differently from the regular (named) members in regard to the `Option` type. The `Option` type gives no additional meaning to the member annotated with `#[builder(finish_fn)]`. Thus, it is matched by the type pattern of `on(..., into)` and wrapped with `impl Into>` as any other type. + +::: tip + +In general, it's not recommended to annotate optional members with `#[builder(finish_fn)]` because you can't omit setting them using the positional function call syntax. + +::: diff --git a/website/reference/builder/member/into.md b/website/reference/builder/member/into.md new file mode 100644 index 00000000..81835500 --- /dev/null +++ b/website/reference/builder/member/into.md @@ -0,0 +1,103 @@ + +# `into` + +**Applies to:** + +Changes the signature of the generated setters to accept [`impl Into`](https://doc.rust-lang.org/stable/std/convert/trait.Into.html), where `T` is the type of the member. + +For [optional members](../../../guide/optional-members), the `maybe_{member}()` setter method will accept an `Option>` type instead of just `Option`. + +For members that use `#[builder(default = expression)]`, the `expression` will be converted with `Into::into`. + +This parameter is often used with the `String` type, which allows you to pass `&str` into the setter without calling `.to_owned()` or `.to_string()` on it. + +See the ["Into Conversions In-Depth"](../../../guide/patterns/into-conversions-in-depth) page that shows the common patterns and antipatterns of `impl Into`. + +**Example:** + +::: code-group + +```rust [Struct field] +use bon::Builder; + +#[derive(Builder)] +struct Example { + #[builder(into)] // [!code highlight] + name: String, + + #[builder(into)] // [!code highlight] + description: Option, + + // The value passed to `default = ...` is converted with `into` as well // [!code highlight] + #[builder(into, default = "anon")] // [!code highlight] + group: String +} + +Example::builder() + // We can pass `&str` because the setters accept `impl Into` // [!code highlight] + .name("Bon") // [!code highlight] + .description("Awesome crate 🐱. Consider giving it a star on Github ⭐") // [!code highlight] + // We can pass `Option<&str>` to `maybe_` methods because they accept // [!code highlight] + // `Option>` // [!code highlight] + .maybe_group(Some("Favourites")) // [!code highlight] + .build(); +``` + +```rust [Free function argument] +use bon::builder; + +#[builder] +fn example( + #[builder(into)] // [!code highlight] + name: String, + + #[builder(into)] // [!code highlight] + description: Option, + + // The value passed to `default = ...` is converted with `into` as well // [!code highlight] + #[builder(into, default = "anon")] // [!code highlight] + group: String +) {} + +example() + // We can pass `&str` because the setters accept `impl Into` // [!code highlight] + .name("Bon") // [!code highlight] + .description("Awesome crate 🐱. Consider giving it a star on Github ⭐") // [!code highlight] + // We can pass `Option<&str>` to `maybe_` methods because they accept // [!code highlight] + // `Option>` // [!code highlight] + .maybe_group(Some("Favourites")) // [!code highlight] + .call(); +``` + +```rust [Associated method argument] +use bon::bon; + +struct Example; + +#[bon] +impl Example { + #[builder] + fn example( + #[builder(into)] // [!code highlight] + name: String, + + #[builder(into)] // [!code highlight] + description: Option, + + // The value passed to `default = ...` is converted with `into` as well // [!code highlight] + #[builder(into, default = "anon")] // [!code highlight] + group: String + ) {} +} + +Example::example() + // We can pass `&str` because the setters accept `impl Into` // [!code highlight] + .name("Bon") // [!code highlight] + .description("Awesome crate 🐱. Consider giving it a star on Github ⭐") // [!code highlight] + // We can pass `Option<&str>` to `maybe_` methods because they accept // [!code highlight] + // `Option>` // [!code highlight] + .maybe_group(Some("Favourites")) // [!code highlight] + .call(); +``` + +::: diff --git a/website/reference/builder/member/name.md b/website/reference/builder/member/name.md new file mode 100644 index 00000000..20198b88 --- /dev/null +++ b/website/reference/builder/member/name.md @@ -0,0 +1,62 @@ +# `name` + +**Applies to:** + +Overrides the name of the member used in the builder's API. This is most useful when with struct syntax (`#[derive(Builder)]`) where you'd like to use a different name for the field internally. For functions this attribute makes less sense since it's easy to just create a variable named differently `let new_name = param_name;`. However, this attribute is still supported on function arguments. + +**Example:** + +::: code-group + +```rust [Struct field] +use bon::Builder; + +#[derive(Builder)] +struct Player { + #[builder(name = rank)] // [!code highlight] + level: u32 +} + +Player::builder() + .rank(10) // [!code highlight] + .build(); +``` + +```rust [Free function argument] +use bon::builder; + +#[builder] +fn player( + #[builder(name = rank)] // [!code highlight] + level: u32 +) {} + +player() + .rank(10) // [!code highlight] + .call(); +``` + +```rust [Associated method argument] +use bon::bon; + +struct Player { + level: u32, +} + +#[bon] +impl Player { + #[builder] + fn new( + #[builder(name = rank)] // [!code highlight] + level: u32 + ) -> Self { + Self { level } + } +} + +Player::builder() + .rank(10) // [!code highlight] + .build(); +``` + +::: diff --git a/website/reference/builder/member/skip.md b/website/reference/builder/member/skip.md new file mode 100644 index 00000000..5f83b639 --- /dev/null +++ b/website/reference/builder/member/skip.md @@ -0,0 +1,66 @@ +# `skip` + +**Applies to:** + +Skips generating setters for the member. This hides the member from the generated builder API, so the caller can't set its value. + +The value for the member will be computed based on the form of the attribute: + +| Form | How value for the member is computed | +| ------------------------------- | ------------------------------------ | +| `#[builder(skip)]` | `Default::default()` | +| `#[builder(skip = expression)]` | `expression` | + +**Example:** + +```rust +use bon::Builder; + +#[derive(Builder)] +struct User { + #[builder(skip)] // [!code highlight] + level: u32, + + // Any complex expression is accepted // [!code highlight] + #[builder(skip = "anon".to_owned())] // [!code highlight] + name: String, +} + +let user = User::builder() + // There are no `level`, and `name` setters generated // [!code highlight] + .build(); + +assert_eq!(user.level, 0); +assert_eq!(user.name, "anon"); +``` + +You can also use the values of other members by referencing their names in the `skip` expression. All members are initialized in the order of their declaration. It means only those members that are declared earlier (higher) in the code are available to the `skip` expression. + +**Example:** + +```rust +use bon::Builder; + +#[derive(Builder)] +struct Example { + member_1: u32, + + // Note that here we don't have access to `member_3` + // because it's declared (and thus initialized) later + #[builder(skip = 2 * member_1)] + member_2: u32, + + #[builder(skip = member_2 + member_1)] + member_3: u32, +} + +let example = Example::builder() + .member_1(3) + .build(); + +assert_eq!(example.member_1, 3); +assert_eq!(example.member_2, 6); +assert_eq!(example.member_3, 9); +``` + +This attribute is not supported with free function arguments or associated method arguments because it's simply unnecessary there and can easier be expressed with local variables. diff --git a/website/reference/builder/member/start-fn.md b/website/reference/builder/member/start-fn.md new file mode 100644 index 00000000..aaea3ff2 --- /dev/null +++ b/website/reference/builder/member/start-fn.md @@ -0,0 +1,106 @@ +# `start_fn` + +**Applies to:** + +Makes the member a positional argument on the starting function that creates the builder. + +The ordering of members annotated with `#[builder(start_fn)]` matters! They will appear in the same order relative to each other in the starting function signature. They must also be declared at the top of the members' list. + +This ensures a consistent initialization order, and it makes these members available for expressions in `#[builder(default/skip = ...)]` for regular members that follow them. + +::: tip + +Don't confuse this with the top-level [`#[builder(start_fn = ...)]`](#start-fn) attribute that can be used to configure the name and visibility of the starting function. You'll likely want to use it in combination with this member-level attribute to define a better name for the starting function. + +::: + +**Example:** + +::: code-group + +```rust [Struct field] +use bon::Builder; + +#[derive(Builder)] +// Top-level attribute to give a better name for the starting function // [!code highlight] +#[builder(start_fn = with_coordinates)] // [!code highlight] +struct Treasure { + // Member-level attribute to mark the member as // [!code highlight] + // a parameter of `with_coordinates()` // [!code highlight] + #[builder(start_fn)] // [!code highlight] + x: u32, + + #[builder(start_fn)] // [!code highlight] + y: u32, + + label: Option, +} + +let treasure = Treasure::with_coordinates(2, 9) // [!code highlight] + .label("knowledge".to_owned()) + .build(); + +assert_eq!(treasure.x, 2); +assert_eq!(treasure.y, 9); +assert_eq!(treasure.label.as_deref(), Some("knowledge")); +``` + +```rust [Free function] +use bon::builder; + +#[builder] +fn mark_treasure_at( + #[builder(start_fn)] // [!code highlight] + x: u32, + + #[builder(start_fn)] // [!code highlight] + y: u32, + + label: Option, +) {} + +mark_treasure_at(2, 9) + .label("knowledge".to_owned()) + .call(); +``` + +```rust [Associated method] +use bon::bon; + +struct Navigator {} + +#[bon] +impl Navigator { + #[builder] + fn mark_treasure_at( + &self, + + #[builder(start_fn)] // [!code highlight] + x: u32, + + #[builder(start_fn)] // [!code highlight] + y: u32, + + label: String, + ) {} +} + +let navigator = Navigator {}; + +navigator + .mark_treasure_at(2, 9) + .label("knowledge".to_owned()) + .call(); +``` + +::: + +You can also combine this attribute with [`#[builder(into)]`](./into) or [`#[builder(on(..., into))]`](../top-level/on) to add an into conversion for the parameter. + +Importantly, `Into` conversions for such members work slightly differently from the regular (named) members in regard to the `Option` type. The `Option` type gives no additional meaning to the member annotated with `#[builder(start_fn)]`. Thus, it is matched by the type pattern of `on(..., into)` and wrapped with `impl Into>` as any other type. + +::: tip + +In general, it's not recommended to annotate optional members with `#[builder(start_fn)]` because you can't omit setting them using the positional function call syntax. + +::: diff --git a/website/reference/builder/top-level/builder-type.md b/website/reference/builder/top-level/builder-type.md new file mode 100644 index 00000000..bffd9dc2 --- /dev/null +++ b/website/reference/builder/top-level/builder-type.md @@ -0,0 +1,95 @@ +# `builder_type` + +**Applies to:** + +Overrides the name of the generated builder struct. + +The default naming pattern is the following: + +| Underlying item | Naming pattern | +| -------------------------- | --------------------------------------------- | +| Struct | `{StructName}Builder` | +| `StructName::new()` method | `{StructName}Builder` | +| Free function | `{PascalCaseFunctionName}Builder` | +| Associated method | `{SelfTypeName}{PascalCaseMethodName}Builder` | + +The attribute expects the desired builder type identifier as its input. + +### `vis` + +**Example:** + +::: code-group + +```rust [Struct] +use bon::Builder; + +#[derive(Builder)] +#[builder(builder_type = MyBuilder)] // [!code highlight] +struct Brush {} + +let builder: MyBuilder = Brush::builder(); +``` + +```rust [Free function] +use bon::builder; + +#[builder(builder_type = MyBuilder)] // [!code highlight] +fn brush() {} + +let builder: MyBuilder = brush(); +``` + +```rust [Associated method] +use bon::bon; + +struct Brush; + +#[bon] +impl Brush { + #[builder(builder_type = MyBuilder)] // [!code highlight] + fn new() -> Self { + Self + } +} + +let builder: MyBuilder = Brush::builder(); +``` + +::: + +You'll usually want to override the builder type name when you already have such a name in scope. For example, if you have a struct and a function with the same name annotated with `#[builder]`: + +::: code-group + +```rust compile_fail [Errored] +use bon::{builder, Builder}; + +#[derive(Builder)] // [!code error] +struct Brush {} + +#[builder] // [!code error] +fn brush() {} + +// `BrushBuilder` builder type name was generated for both +// the struct and the function. This is a compile error +let builder: BrushBuilder = Brush::builder(); +let builder: BrushBuilder = brush(); +``` + +```rust [Fixed] +use bon::{builder, Builder}; + +#[derive(Builder)] +#[builder(builder_type = MyBuilder)] // [!code highlight] +struct Brush {} + +#[builder] +fn brush() {} + +// Now builder types are named differently +let builder: MyBuilder = Brush::builder(); +let builder: BrushBuilder = brush(); +``` + +::: diff --git a/website/reference/builder/top-level/derive.md b/website/reference/builder/top-level/derive.md new file mode 100644 index 00000000..c216651f --- /dev/null +++ b/website/reference/builder/top-level/derive.md @@ -0,0 +1,141 @@ +# `derive` + +**Applies to:** + +*⚠️ Do not confuse this with `derive(bon::Builder)`⚠️* + +Generates additional derives on the builder struct itself. The syntax is similar to the regular `#[derive(...)]` attribute, but it must be wrapped in `#[builder(derive(...))]`. Expects one or more of the supported derives separated by commas. + + +The following derives are supported: `Clone`, `Debug`. + +::: warning +The format of the `Debug` output of the builder is not stable, and it may change between patch versions of `bon`. +::: + +**Example:** + +::: code-group + +```rust [Struct] +use bon::Builder; + +#[derive(Builder)] +#[builder(derive(Clone, Debug))] // [!code highlight] +struct Example { + name: String, + is_admin: bool, + level: Option, +} + +let builder = Example::builder().name("Bon".to_owned()); + +// We can clone the builder // [!code highlight] +let builder = builder.clone(); // [!code highlight] + +// We can debug-format the builder // [!code highlight] +let builder_debug = format!("{builder:?}"); // [!code highlight] + +assert_eq!( + builder_debug, + // Only the fields that were set will be output + r#"ExampleBuilder { name: "Bon" }"# +); + +// Finish building +let example = builder.is_admin(true).build(); +``` + +```rust [Free function] +use bon::builder; + +#[builder(derive(Clone, Debug))] // [!code highlight] +fn example( + name: String, + is_admin: bool, + level: Option, +) {} + +let builder = example().name("Bon".to_owned()); + +// We can clone the builder // [!code highlight] +let builder = builder.clone(); // [!code highlight] + +// We can debug-format the builder // [!code highlight] +let builder_debug = format!("{builder:?}"); // [!code highlight] + +assert_eq!( + builder_debug, + // Only the fields that were set will be output + r#"ExampleBuilder { name: "Bon" }"# +); + +// Finish building +builder.is_admin(true).call(); +``` + +```rust [Associated method] +use bon::bon; + +#[derive(Debug)] +struct Example; + +#[bon] +impl Example { + #[builder(derive(Clone, Debug))] // [!code highlight] + fn method( + name: String, + is_admin: bool, + level: Option, + ) {} + + #[builder(derive(Debug))] + fn method_with_self(&self) {} +} + +let builder = Example::method().name("Bon".to_owned()); + +// We can clone the builder // [!code highlight] +let builder = builder.clone(); // [!code highlight] + +// We can debug-format the builder // [!code highlight] +let builder_debug = format!("{builder:?}"); // [!code highlight] + +assert_eq!( + builder_debug, + // Only the fields that were set will be output + r#"ExampleMethodBuilder { name: "Bon" }"# +); + +// Finish building +builder.is_admin(true).call(); + +// The debug output of the builder for methods with `self` includes +// the special `self` field with the receiver. +assert_eq!( + format!("{:?}", Example.method_with_self()), + "ExampleMethodWithSelfBuilder { self: Example }" +) +``` + +::: + +## Compile errors + +_Requires_ that all members of the builder including the receiver (if this is a builder for an associated method) implement the target trait. For example, this doesn't compile because not all members implement `Clone`: + +**Example:** + +```rust compile_fail +use bon::Builder; + +struct NonClone; + +#[derive(Builder)] +#[builder(Clone)] +struct Example { + // Doesn't derive `Clone`, so this code doesn't compile // [!code error] + non_clone NonClone, // [!code error] + cloneable: u32 +} +``` diff --git a/website/reference/builder/top-level/finish-fn.md b/website/reference/builder/top-level/finish-fn.md new file mode 100644 index 00000000..2fc27f78 --- /dev/null +++ b/website/reference/builder/top-level/finish-fn.md @@ -0,0 +1,63 @@ +# `finish_fn` + +**Applies to:** + +This attribute allows overriding the name of the generated builder's method that finishes the building process. + +**Example:** + +::: code-group + +```rust [Struct] +use bon::Builder; + +#[derive(Builder)] +#[builder(finish_fn = assemble)] // [!code highlight] +struct Article { + id: u32 +} + +let article = Article::builder() + .id(42) + .assemble(); // [!code highlight] + +assert_eq!(article.id, 42); +``` + +```rust [Free function] +use bon::builder; + +#[builder(finish_fn = send)] // [!code highlight] +fn get_article(id: u32) -> String { + format!("Some article with id {id}") +} + +let response = get_article() + .id(42) + .send(); // [!code highlight] + +assert_eq!(response, "Some article with id 42"); +``` + +```rust [Associated method] +use bon::bon; + +struct ArticlesClient; + +#[bon] +impl ArticlesClient { + #[builder(finish_fn = send)] // [!code highlight] + fn get_article(&self, id: u32) -> String { + format!("Some article with id {id}") + } +} + +let response = ArticlesClient + .get_article() + .id(42) + .send(); // [!code highlight] + +assert_eq!(response, "Some article with id 42"); +``` + +::: diff --git a/website/reference/builder/top-level/on.md b/website/reference/builder/top-level/on.md new file mode 100644 index 00000000..1afd13d0 --- /dev/null +++ b/website/reference/builder/top-level/on.md @@ -0,0 +1,137 @@ +# `on` + +**Applies to:** + +Applies the given builder attributes to all members that match a type pattern. For example, you can automatically apply `#[builder(into)]` to all members of type `String` this way: + +::: code-group + +```rust [Struct] +use bon::Builder; + +#[derive(Builder)] +#[builder(on(String, into))] +struct Example { + id: String, + name: String, + level: u32, +} + +Example::builder() + // `id` and `name` accept `impl Into` because // [!code highlight] + // `on` automatically added `#[builder(into)]` for them // [!code highlight] + .id("e-1111") + .name("Bon") + // `u32` doesn't match the `String` type pattern, // [!code highlight] + // so `#[builder(into)]` was not applied to it // [!code highlight] + .level(100) + .build(); +``` + +```rust [Free function] +use bon::builder; + +#[builder(on(String, into))] +fn example( + id: String, + name: String, + level: u32, +) {} + +example() + // `id` and `name` accept `impl Into` because // [!code highlight] + // `on` automatically added `#[builder(into)]` for them // [!code highlight] + .id("e-1111") + .name("Bon") + // `u32` doesn't match the `String` type pattern, // [!code highlight] + // so `#[builder(into)]` was not applied to it // [!code highlight] + .level(100) + .call(); +``` + +```rust [Associated method] +use bon::bon; + +struct Example; + +#[bon] +impl Example { + #[builder(on(String, into))] + fn example( + id: String, + name: String, + level: u32, + ) {} +} + +Example::example() + // `id` and `name` accept `impl Into` because // [!code highlight] + // `on` automatically added `#[builder(into)]` for them // [!code highlight] + .id("e-1111") + .name("Bon") + // `u32` doesn't match the `String` type pattern, // [!code highlight] + // so `#[builder(into)]` was not applied to it // [!code highlight] + .level(100) + .call(); +``` + +::: + +This attribute must be of form `on(type_pattern, attributes)`. + +- `type_pattern` - type that will be compared with the types of the members. The types are compared textually. For example, `String` doesn't match `std::string::String`. You can use `_` to mark parts of the type to ignore when matching. For example, `Vec<_>` matches `Vec` or `Vec`. Lifetimes are ignored during matching. + +- `attributes` - for now, the only attribute supported in the `attributes` position is [`into`](../member/into). It sets `#[builder(into)]` for members that match the `type_pattern`. + +If you want to apply the `attributes` to all members, you can use the `_` type pattern that matches any type. For example, `#[builder(on(_, into))]`. + +For optional members the underlying type is matched ignoring the `Option` wrapper. + +**Example:** + +```rust +use bon::Builder; + +#[derive(Builder)] +#[builder(on(String, into))] +struct Example { + name: String, + description: Option, + + #[builder(default)] + alias: String +} + +Example::builder() + .name("Bon") + // These members also match the `String` type pattern, + // so `#[builder(into)]` was applied to them + .description("accepts an `impl Into` here") + .alias("builder") + .build(); +``` + +You can specify `on(...)` multiple times. + +**Example:** + +```rust +use bon::Builder; +use std::path::PathBuf; + +#[derive(Builder)] +#[builder(on(String, into), on(PathBuf, into))] +struct Example { + name: String, + path: PathBuf, + level: u32, +} + +Example::builder() + .name("accepts `impl Into`") + .path("accepts/impl/into/PathBuf") + // This member doesn't match either `String` or `PathBuf`, + // and thus #[builder(into)] was not applied to it + .level(100) + .build(); +``` diff --git a/website/reference/builder/top-level/start-fn.md b/website/reference/builder/top-level/start-fn.md new file mode 100644 index 00000000..94ee4274 --- /dev/null +++ b/website/reference/builder/top-level/start-fn.md @@ -0,0 +1,175 @@ +# `start_fn` + +**Applies to:** + +Overrides the name, visibility and docs of the function that starts the building process, i.e. returns the builder for the `struct` or `fn`. It is commonly referred to as the "starting function". + +When this attribute is used with `fn` syntax, it additionally [exposes the original function](#exposing-original-function) under the macro. + +**Short syntax** configures just the *name*. + +```attr +#[builder(start_fn = custom_name)] +``` + +**Long syntax** provides more flexibility. All keys are optional. + +```attr +#[builder( + start_fn( + name = custom_name, + vis = "pub(crate)", + doc { + /// Custom docs + } + ) +)] +``` + +### `name` + +The default name for the starting function depends on the syntax used. + +| Syntax | Default +| -- | -- | +| `struct` | `builder` +| associated `fn` called `new` | `builder` +| associated `fn` *not* called `new` | `{function_name}` +| free `fn` | `{function_name}` + +### `vis` + +The visibility must be enclosed with quotes. Use `""` or [`"pub(self)"`](https://doc.rust-lang.org/reference/visibility-and-privacy.html#pubin-path-pubcrate-pubsuper-and-pubself) for private visibility. + +The default visibility is the same as the visibility of the [`builder_type`](./builder-type#vis), which in turn, defaults to the visibility of the underlying `struct` or `fn`. + +### `doc` + +Simple documentation is generated by default. The syntax of this attribute expects a block with doc comments. + +## Example + +::: code-group + +```rust [Short syntax] +use bon::Builder; + +#[derive(Builder)] +#[builder(start_fn = init)] // [!code highlight] +struct User { + id: u32 +} + +User::init() // [!code highlight] + .id(42) + .build(); +``` + +```rust [Long syntax] +use bon::Builder; + +// `User::init()` method will have `pub(crate)` visibility // [!code highlight] +// Use `vis = ""` to make it fully private instead // [!code highlight] +#[derive(Builder)] +#[builder(start_fn(name = init, vis = "pub(crate)"))] // [!code highlight] +pub struct User { + id: u32 +} + +User::init() // [!code highlight] + .id(42) + .build(); +``` + +::: + + +## Exposing original function + +By default, when you place `#[builder]` on a function, that original function is modified by the macro to make it hidden such that only builder syntax remains available. Specifically, the macro does the following by default: + +- Prepends the prefix `__orig_` to the original function's name. +- Changes the visibility of the original function to private. +- Adds `#[doc(hidden)]` to the function. + +If you specify the `start_fn` attribute on a function, the macro skips hiding the original function. It means the starting function no longer replaces the original function. This way both the original positional function and the starting function with the builder syntax are exposed. + +This can be used to preserve backwards compatibility for code that uses the original function directly. + +::: tip NOTE + +The [`name`](#name) parameter is **required** when `start_fn` is used with `fn` syntax. + +::: + +**Example:** + +::: code-group + +```rust [Free function] +use bon::builder; + +#[builder(start_fn = example_builder)] // [!code ++] +fn example(x: u32, y: u32) {} + +// The original positional function is still available +example(1, 2); + +// The builder syntax is also available under the given name +example_builder() + .x(1) + .y(2) + .call(); +``` + +```rust [Associated method] +use bon::bon; + +struct Example; + +#[bon] +impl Example { + #[builder(start_fn = builder)] + fn new(x: u32, y: u32) -> Self { + Example + } + + #[builder(start_fn = method_builder)] + fn method(&self, x: u32, y: u32) {} +} + +// The original method `new` is still available +Example::new(1, 2); + +// The builder syntax is also available under the given name +let example = Example::builder() + .x(1) + .y(2) + .build(); + +// The original `method` is still available +example.method(1, 2); + +// The builder syntax for the `method` is also available under the given name +example.method_builder() + .x(1) + .y(2) + .call(); +``` + +::: + +One interesting quirk is that by default the associated method named `new` already generates a starting function under the name `builder`. However, by default, the original method `new` also becomes hidden. By specifying the `#[builder(start_fn = builder)]` explicitly the original method `new` remains exposed. + +--- + +Given that `#[builder(start_fn)]` on a function behaves *[additively](https://doc.rust-lang.org/cargo/reference/features.html#feature-unification)*, you can provide builder syntax as an opt-in cargo feature of your crate. + +**Example:** + +```rust +#[cfg_attr(feature = "bon", bon::builder(start_fn = function_builder))] +fn function() {} +``` + +If you use this syntax, the `function()` always remains available with or without the `bon` feature enabled. diff --git a/website/v1/guide/compatibility.md b/website/v1/guide/compatibility.md index 515cd37a..09760bf5 100644 --- a/website/v1/guide/compatibility.md +++ b/website/v1/guide/compatibility.md @@ -200,7 +200,3 @@ example_builder() .y(2) .call(); ``` - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/v1/guide/limitations.md b/website/v1/guide/limitations.md index 51c8a43d..3dc6a9c1 100644 --- a/website/v1/guide/limitations.md +++ b/website/v1/guide/limitations.md @@ -80,7 +80,3 @@ If you have a strong use case that requires full support for `const`, feel free Conditionally-compiled members aren't supported yet. The blocker for this feature is a lack of support for attributes in `where` bounds in the language. See [rust-lang/rust/#115590](https://github.com/rust-lang/rust/issues/115590) for details. [open an issue]: https://github.com/elastio/bon/issues - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/v1/guide/optional-members.md b/website/v1/guide/optional-members.md index 204bca3d..1397c2dd 100644 --- a/website/v1/guide/optional-members.md +++ b/website/v1/guide/optional-members.md @@ -82,7 +82,3 @@ example() .call(); ``` - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/v1/guide/overview.md b/website/v1/guide/overview.md index 5cb6b5a0..384f99c3 100644 --- a/website/v1/guide/overview.md +++ b/website/v1/guide/overview.md @@ -214,7 +214,3 @@ If you can't figure something out, consult the docs and maybe use that search ` This project was heavily inspired by such awesome crates as [`buildstructor`](https://docs.rs/buildstructor), [`typed-builder`](https://docs.rs/typed-builder) and [`derive_builder`](https://docs.rs/derive_builder). This crate was designed with many lessons learned from them. See [alternatives](./alternatives) for comparison. - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/v1/reference/builder.md b/website/v1/reference/builder.md index f5b41c2e..47f5ec3f 100644 --- a/website/v1/reference/builder.md +++ b/website/v1/reference/builder.md @@ -753,7 +753,3 @@ assert_eq!(user.permissions, ["read"]); ::: [automatic `Into` conversion qualification rules]: ../guide/into-conversions#types-that-qualify-for-an-automatic-into-conversion - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/v2/guide/compatibility.md b/website/v2/guide/compatibility.md index 68daffbe..e7df67a2 100644 --- a/website/v2/guide/compatibility.md +++ b/website/v2/guide/compatibility.md @@ -201,7 +201,3 @@ example_builder() .y(2) .call(); ``` - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/v2/guide/limitations.md b/website/v2/guide/limitations.md index bb83ca2c..21b20faf 100644 --- a/website/v2/guide/limitations.md +++ b/website/v2/guide/limitations.md @@ -143,7 +143,3 @@ It's possible to place `#[builder]` on top of a `const fn`, but the generated bu If you have a strong use case that requires full support for `const`, feel free to [open an issue]. We'll figure something out for sure 🐱. [open an issue]: https://github.com/elastio/bon/issues - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/v2/guide/optional-members.md b/website/v2/guide/optional-members.md index 814512c0..61d2e495 100644 --- a/website/v2/guide/optional-members.md +++ b/website/v2/guide/optional-members.md @@ -104,8 +104,3 @@ assert_eq!(result, 8); ## Conditional building Now that you know how optional members work you can check out the ["Conditional building" patterns](./patterns/conditional-building) or continue studying other features of `bon` by following the "Next page" link at the bottom. - - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/v2/guide/overview.md b/website/v2/guide/overview.md index a86ddfa7..5370fdab 100644 --- a/website/v2/guide/overview.md +++ b/website/v2/guide/overview.md @@ -303,7 +303,3 @@ If you can't figure something out, consult the docs and maybe use that search ` This project was heavily inspired by such awesome crates as [`buildstructor`](https://docs.rs/buildstructor), [`typed-builder`](https://docs.rs/typed-builder) and [`derive_builder`](https://docs.rs/derive_builder). This crate was designed with many lessons learned from them. See [alternatives](./alternatives) for comparison. - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/v2/guide/patterns/conditional-building.md b/website/v2/guide/patterns/conditional-building.md index ea8f0cea..d7b373a0 100644 --- a/website/v2/guide/patterns/conditional-building.md +++ b/website/v2/guide/patterns/conditional-building.md @@ -138,7 +138,3 @@ if 2 + 2 == 4 { // Pass the variables to the builder now // ... ``` - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/v2/guide/patterns/into-conversions-in-depth.md b/website/v2/guide/patterns/into-conversions-in-depth.md index 3db8d730..27406257 100644 --- a/website/v2/guide/patterns/into-conversions-in-depth.md +++ b/website/v2/guide/patterns/into-conversions-in-depth.md @@ -373,7 +373,3 @@ Example::builder() This is quite subjective, but `impl Into` is a bit harder to read than just `T`. It makes the signature of the setter slightly bigger and requires you to understand what the `impl Trait` does, and what its implications are. If you want to keep your code simpler and more accessible (especially for beginner rustaceans), just avoid the `Into` conversions. - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments diff --git a/website/v2/reference/builder.md b/website/v2/reference/builder.md index d961aea2..d91fe7e2 100644 --- a/website/v2/reference/builder.md +++ b/website/v2/reference/builder.md @@ -1307,7 +1307,3 @@ Importantly, `Into` conversions for such members work slightly differently from In general, it's not recommended to annotate optional members with `#[builder(start_fn)]` because you can't omit setting them using the positional function call syntax. ::: - -*[Member]: Struct field or a function argument -*[member]: Struct field or a function argument -*[members]: Struct fields or function arguments