Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upStrengthen the recommendation to use &mut self builders #81
Comments
This comment has been minimized.
This comment has been minimized.
Kixunil
commented
May 31, 2017
|
Why? Consuming builder can avoid misuse and unwrapping. |
This comment has been minimized.
This comment has been minimized.
|
Explanation from Aaron Turon in the reqwest meeting:
Here are some ergonomic issues with consuming builders. A consuming builder would be protecting against largely hypothetical misuse, while a &mut self builder would be providing very practical ergonomic benefits. Reassignment is annoying#86 makes this harder to forget, but no less inconvenient. let mut builder = Builder::new();
if let Some(x) = x {
// &mut self builder:
builder.x(x);
// self builder:
builder = builder.x(x);
}
builder.build()Hard to give back ownership of the builder after errorIt might be possible to enable this by having an accessor on the error type that gives back ownership of the builder, but then you tend to run into Send+Sync trouble. See #82. let mut builder = Builder::new();
// &mut self builder
if let Err(err) = builder.x(x) {
warn!("ignoring x: {}", err);
}
// self builder
builder = match builder.x(x) {
Ok(builder) => builder,
Err(err) => {
warn!("ignoring x: {}", err);
/* ??? */
}
};
builder.build() |
This comment has been minimized.
This comment has been minimized.
Kixunil
commented
May 31, 2017
|
My perfectionism dislikes it but you're probably right. |
dtolnay
referenced this issue
Sep 3, 2017
Closed
Resolves #334 - Bumping Deserializer's default recursion limit to 1024 #346
dtolnay
added
the
clarification
label
Sep 18, 2017
dtolnay
added
the
hacktoberfest
label
Oct 1, 2017
This comment has been minimized.
This comment has been minimized.
dtantsur
commented
Jan 2, 2018
|
A counter-example (reflecting my actual issues with reqwest): With self (pseudo-rust): fn custom_request(method: Method, url: Url) -> RequestBuilder {
let extra_hdrs = calculate_some_headers();
make_client().request(method, url).headers(extra_hdrs)
}With &mut self: fn custom_request(method: Method, url: Url) -> RequestBuilder {
let extra_hdrs = calculate_some_headers();
let mut builder = make_client().request(method, url);
{
// make unused_results lint happy; we cannot return _unused because it's a reference
let _unused = builder.headers(extra_hdrs);
}
builder
} |
seanmonstar
referenced this issue
Feb 18, 2018
Closed
Change RequestBuilder methods to own a builder #260
This comment has been minimized.
This comment has been minimized.
xfix
commented
Feb 20, 2018
•
|
I disagree with this guideline, I already wrote my issues with it as far reqwest is concerned at seanmonstar/reqwest#260 (comment). |
This comment has been minimized.
This comment has been minimized.
seanmonstar
commented
Feb 21, 2018
|
I am also no longer convinced that this recommendation makes sense in all cases. Specifically, in
|
This comment has been minimized.
This comment has been minimized.
awestlake87
commented
Apr 29, 2018
|
Something I've kinda wondered about is whether or not it would be reasonable to make a (for lack of a better word) "boomerang" fn, one that can temporarily take ownership of self, but also ensure that self is valid and usable after the call. If the biggest argument against by-value builders is ergonomics, then the "boomerang" fn might provide the best of both worlds. struct MyBuilder {
a: u32,
b: u32,
c: u32,
}
impl MyBuilder {
fn new() -> Self {
Self { a: 0, b: 0, c: 0 }
}
boomerang a(self, a: u32) {
self = Self { a: a, ..self }
}
boomerang b(self, b: u32) {
self = Self { b: b, ..self }
}
boomerang c(self, c: u32) {
self = Self { c: c, ..self }
}
fn build(self) -> (u32, u32, u32) {
}
}
fn main() {
let builder: MyBuilder::new();
// builder gets passed by-value to a(...), by-value to b(...) then returned to the builder variable.
builder
.a(34)
.b(45);
// builder gets passed by-value to c(...), then finally consumed by build(...).
let tuple = builder
.c(64)
.build();
}
I'm pretty opposed to adding new syntax for it, and it also seems a bit ugly to have "boomerang" act like it returns "self" in some contexts, but leaves it unmoved in others. Basically, I don't like it, but I've also found myself wanting something like it multiple times for the sake of convenience. |
dtolnay commentedMay 31, 2017
This seems like almost always the better alternative. For cases where the terminal method needs ownership of some state in the builder, the state can be stored in an Option that the terminal method can take() out.