Skip to content

Commit ff402e3

Browse files
committed
Add docs for #[builder(derive(Into))]
1 parent 40f94a8 commit ff402e3

File tree

1 file changed

+67
-15
lines changed
  • website/src/reference/builder/top-level

1 file changed

+67
-15
lines changed

website/src/reference/builder/top-level/derive.md

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ _⚠️ Do not confuse this with `#[derive(bon::Builder)]`⚠️_
66

77
Generates additional derives for 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 a comma.
88

9-
The following derives are supported: `Clone`, `Debug`.
9+
The following derives are supported: [`Clone`, `Debug`](#clone-and-debug-derives), [`Into`](#into-derive).
1010

1111
::: warning
1212
The format of the `Debug` output of the builder is not stable, and it may change between patch versions of `bon`.
@@ -20,7 +20,7 @@ The format of the `Debug` output of the builder is not stable, and it may change
2020
use bon::Builder;
2121

2222
#[derive(Builder)]
23-
#[builder(derive(Clone, Debug))] // [!code highlight]
23+
#[builder(derive(Clone, Debug, Into))] // [!code highlight]
2424
struct Example {
2525
name: String,
2626
is_admin: bool,
@@ -41,19 +41,24 @@ assert_eq!(
4141
r#"ExampleBuilder { name: "Bon" }"#
4242
);
4343

44-
// Finish building
45-
let example = builder.is_admin(true).build();
44+
let example: Example = builder
45+
.is_admin(true)
46+
// We can use `From<ExampleBuilder>` to `Example` instead of
47+
// calling .build()
48+
.into();
4649
```
4750

4851
```rust [Function]
4952
use bon::builder;
5053

51-
#[builder(derive(Clone, Debug))] // [!code highlight]
54+
#[builder(derive(Clone, Debug, Into))] // [!code highlight]
5255
fn example(
5356
name: String,
5457
is_admin: bool,
5558
level: Option<u32>,
56-
) {}
59+
) -> u32 {
60+
level.unwrap_or(99)
61+
}
5762

5863
let builder = example().name("Bon".to_owned());
5964

@@ -69,8 +74,13 @@ assert_eq!(
6974
r#"ExampleBuilder { name: "Bon" }"#
7075
);
7176

72-
// Finish building
73-
builder.is_admin(true).call();
77+
let example: u32 = builder
78+
.is_admin(true)
79+
// We can use `From<ExampleBuilder>` to `Example` instead of
80+
// calling .build()
81+
.into();
82+
83+
assert_eq!(example, 99);
7484
```
7585

7686
```rust [Method]
@@ -81,12 +91,14 @@ struct Example;
8191

8292
#[bon]
8393
impl Example {
84-
#[builder(derive(Clone, Debug))] // [!code highlight]
94+
#[builder(derive(Clone, Debug, Into))] // [!code highlight]
8595
fn method(
8696
name: String,
8797
is_admin: bool,
8898
level: Option<u32>,
89-
) {}
99+
) -> u32 {
100+
99
101+
}
90102

91103
#[builder(derive(Debug))]
92104
fn method_with_self(&self) {}
@@ -106,8 +118,14 @@ assert_eq!(
106118
r#"ExampleMethodBuilder { name: "Bon" }"#
107119
);
108120

109-
// Finish building
110-
builder.is_admin(true).call();
121+
122+
let example: u32 = builder
123+
.is_admin(true)
124+
// We can use `From<ExampleBuilder>` to `Example` instead of
125+
// calling .build()
126+
.into();
127+
128+
assert_eq!(example, 99);
111129

112130
// The debug output of the builder for methods with `self` includes
113131
// the special `self` field with the receiver.
@@ -119,9 +137,11 @@ assert_eq!(
119137

120138
:::
121139

122-
## Generic types handling
140+
## `Clone` and `Debug` derives
141+
142+
### Generic types handling
123143

124-
If the underlying `struct` or `fn` contains generic type parameters, then the generated impl block will include a `where` bound requiring the respective trait (`Clone` or `Debug`) to be implemented by all of them. This follows the behaviour of the [standard `derive` macros](https://doc.rust-lang.org/std/clone/trait.Clone.html#derivable).
144+
If the underlying `struct` or `fn` contains generic type parameters, then the generated impl block will include a `where` bound requiring the respective trait to be implemented by all of them. This follows the behaviour of the [standard `derive` macros](https://doc.rust-lang.org/std/clone/trait.Clone.html#derivable).
125145

126146
This works fine in most cases, but sometimes the generated bounds may be overly restrictive. To fix that, you can manually specify the bounds using the syntax `#[builder(derive(Trait(bounds(...))))]`, where `...` is a comma-separated list of `where` bounds.
127147

@@ -177,7 +197,7 @@ If you'd like to know why this attribute is this dumb and doesn't just add a `wh
177197

178198
:::
179199

180-
## Compile errors
200+
### Compile errors
181201

182202
_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`:
183203

@@ -196,3 +216,35 @@ struct Example {
196216
cloneable: u32
197217
}
198218
```
219+
220+
## `Into` derive
221+
222+
Somewhat obviously, but `Into` derive actually generates a `From` implementation, providing the `Into` trait implementation automatically via the [blanket `impl` in std](https://doc.rust-lang.org/stable/std/convert/trait.From.html#generic-implementations).
223+
224+
::: details See an example of what code it generates:
225+
226+
(you can even write it manually actually)
227+
228+
```rust
229+
use bon::Builder;
230+
231+
#[derive(Builder)]
232+
struct Example {
233+
x1: String,
234+
}
235+
236+
// This is what #[builder(derive(Into))] would generate:
237+
impl<S: example_builder::IsComplete> From<ExampleBuilder<S>> for Example {
238+
fn from(builder: ExampleBuilder<S>) -> Self {
239+
ExampleBuilder::build(builder)
240+
}
241+
}
242+
```
243+
244+
:::
245+
246+
Note that `#[builder(derive(Into))]` is quite limited. Here are some things it doesn't support:
247+
248+
- `async` functions, because `From::from()` is synchronous
249+
- `unsafe` functions, because `From::from()` is safe
250+
- Members marked with [`#[builder(finish_fn)]`](../member/finish_fn) because `From::from()` doesn't accept arguments

0 commit comments

Comments
 (0)