New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Move Builders to by-value #191
Comments
Personally, I don't have a strong opinion between I disagree with the ergonomics points. While it requires less lines of code, as you pointed out, being forced to assign the new builder back to the original variable always felt weird to me. Also, when writing fns that mutate the builder, one now always has to include the builder as a return value. That said, I personally don't care enough about the differences between the two styles. What I do care about is:
The Rust API guidelines recommend I also don't believe that stylistic preference is enough of an argument to support providing two separate idioms. |
I find the guidelines on builders a bit too strict. When I write builders, I tend to follow this mental guideline:
I don't see a big problem with using different approaches depending on the situation, though generally I agree we should weigh very carefully whether the benefits of alternate approach outweighs breaking conventions. |
While I actually think the correctness points are more important, I think the ergonomic gains are a net win, since we can actually remove the weirdness.
I think we can fix this weirdness by include by-ref // by-value
let mut builder = Request::builder()
.uri("/foo/bar")
.method("POST");
if body_len != 0 {
builder.set_header("content-length", body.to_string());
}
builder
.body(body)
.unwrap()
Another reason we should add by-ref
As I pointed out in a comment in the same issue, I'm convinced the guidelines are wrong. Additionally, the guidelines aren't cared for frequently, and so tend to just kind of whither. In the case where builders can be reused, I think In the case of http's request and response builders, this is not the case. They cannot be reused, and you have no way of even protecting against a library mutator that takes |
Given the size of |
The |
One advantage with moving the core types to a new crate, we could bump the builders sooner. |
@seanmonstar wouldn't a by-value builder also eliminate the need for I find this to be a big ergonomics issue in the current builder |
usage report: I ran into an error in my production application which returned the Luckily, using backtraces it was relatively easy to find out the cause was a missing But yes, I can concur that it would have been nice if this was detected at compile time by making the builder consume itself. edit ah wait, I see this was merged in #332, looking forward to an eventual |
This has been done in 0.2.x. |
I propose that
http::request::Builder
andhttp::response::Builder
should be changed to be by-value builders, instead of by-ref as they currently are. This may be a little controversial, but I believe there's good reason to do so, and my proposal still includes ways to mutate builders with&mut self
for cases that want it.Motivation
let mut b = Request::builder()
,b.chain().chain();
, and thenb.chainMore().build()
.builder()
to be able to pass it anywhere by value.build
leaves behind a mine in the builder slot, since the builder must consume internal state to build. That means the compiler will not catch any attempts to use the builder after a call tobuild
.&mut Builder
to a function can actually consume the internal builder state, which can result in unexpected panics after having passed it to a modifier where the assumption was it was just set some properties.Improvements
Builder
by value to another function or return it. It is not required to break up the chain to appease the type system.Builder
, they know for certain that it is not a consumed shell that might panic if used more. Using the builder tobuild
requires ownership of it, so supposed modifier functions cannot accidentally consume it.But conditionally chaining on by-value is annoying
First, I'd offer that if we have to pick between two annoying things, dealing with a by-value builder is the less annoying of the two. Compare:
VS
However, as I said in the beginning, we can add methods that mutate on
&mut self
, to allow that pattern for those who want it. Basically, every builder method that mutates self by-value, likeuri
, we could addset_uri(&mut self, ...) -> &mut Self
as a mirror.These by-ref mutators could be chained as well. The only pain is that they couldn't be chained into
body()
, since that would require by-value. However, I've provided above while I feel this is better.Alternative
We could optionally have the by-ref mutates keep the same name, and make the by-value methods have a
with_
prefix. However, this would end up as a bigger breaking change, since all existing code could no longer callbody()
on their chains.Breaking Changes
If we switch to by-value without the prefix, the breaking changes would be these:
&mut Builder
would need to be changed to theset_
versions.&mut Builder
would no longer be able to do so.However, anyone that has the full chain together should not see any breaking change.
The text was updated successfully, but these errors were encountered: