Skip to content
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

Replace error-returning builders with typestate-based builders #330

Open
davidbarsky opened this issue Jul 15, 2019 · 10 comments · May be fixed by #483
Open

Replace error-returning builders with typestate-based builders #330

davidbarsky opened this issue Jul 15, 2019 · 10 comments · May be fixed by #483
Labels
B-rfc Blocked: request for comments. More discussion would help move this along.

Comments

@davidbarsky
Copy link
Contributor

It'd be really handy to replace the current builders with a set of builders for Request, Response, and URI that track the builder's state using some sort of typestate, allowing for compile-time checks that a Request/Response/URI were correctly built.

(Following up from Twitter: https://twitter.com/seanmonstar/status/1149772057212669952)

@seanmonstar seanmonstar added the B-rfc Blocked: request for comments. More discussion would help move this along. label Nov 26, 2019
@kaj
Copy link

kaj commented Nov 6, 2020

Agreed. When looking into why Response::builder().body("Hello") can fail at all, the main reason seems to be that a lot of methods of Builder takes T: TryFrom<A> where A is the actually wanted type, instead of T: Into<A>. This might be convenient at times, but I think they should take Into<A> or even A, and then the caller can decide if a fallible conversion is wanted (and handle the error) or if in infallible conversion or a ready-made actual value should be used instead.

Take the status method for exampe. It might seem nice to be able to write builder.status(304), but that makes it possible to write builder.status(75), which leaves the builder in a broken state. If we instead insisted in builder.status(StatusCode::PERMANENT_REDIRECT) the builder don't need to have a broken state. We could even add a function to enable builder.try_status(75), but that would return a Result<Builder, http::Error>, i.e. either a builder in a good state or no builder at all.

kaj added a commit to kaj/http that referenced this issue Nov 7, 2020
Fixes hyperium#330.  Creating a Response with the builder pattern should not
require handling errors unless opting in by using a `try_`-method
(which returns a `Result<Builder2>`, not a `Builder2` with an interal
error state).

This is really a breaking change to `response::Builder`, but rather
than suggstig a breaking change directly, I introduce an alternative
`response::Builder2`.  If this is liked, the original `Builder` can be
deprecated, and in a later release `Builder2` can be renamed back to
`Builder`.
@kaj
Copy link

kaj commented Nov 24, 2020

@seanmonstar , how does the B-rfc tag work, is there ongoing discussions somewhere else or is this thread the place for discussion? Is there a thread on users.rust-lang.org? Should I open one?

@seanmonstar
Copy link
Member

The tag is meant to say the issue needs more comments about how and if to move forward. The discussion can happen on the issue.

@kaj
Copy link

kaj commented Nov 24, 2020

Ok. I have added some thoughts. Maybe I should add some more motivation / explanation on the linked PR.

kaj added a commit to kaj/http that referenced this issue Nov 30, 2020
Fixes one part of hyperium#330.  This makes it possible to create an `Uri`
using the builder pattern without hanlding errors.

This is really a breaking change to `uri::Builder`, but rather than
suggesting a breaking change directly, I introduce an alternative
`uri::Builder2`. If this is accepted, the original Builder is
deprecated, and in a later release Builder2 can be renamed back to
Builder.

The new `Builder2` is generic on its contents, which starts out as the
empty type `()`.  The parts `Scheme`, `Authority` and `PathAndQuery`
can be added in any combination, and the `build` method is implemented
for valid combinations only.
kaj added a commit to kaj/http that referenced this issue Dec 13, 2020
Fixes one part of hyperium#330. Creating a `Request` with the builder pattern
should not require handling errors unless opting in by using a
`try_`-method (which returns a `Result<Builder2>`, not a `Builder2`
with an internal error state).

This is really a breaking change to `request::Builder`, but rather
than suggesting a breaking change directly, I introduce an alternative
`response::Builder2`. If this is accepted, the original
`response::Builder` is deprecated, and in a later release `Builder2`
can be renamed back to `Builder`.
kaj added a commit to kaj/http that referenced this issue Dec 13, 2020
Fixes one part of hyperium#330. Creating a `Request` with the builder pattern
should not require handling errors unless opting in by using a
`try_`-method (which returns a `Result<Builder2>`, not a `Builder2`
with an internal error state).

This is really a breaking change to `request::Builder`, but rather
than suggesting a breaking change directly, I introduce an alternative
`response::Builder2`. If this is accepted, the original
`response::Builder` is deprecated, and in a later release `Builder2`
can be renamed back to `Builder`.
kaj added a commit to kaj/http that referenced this issue Dec 20, 2020
Fixes hyperium#330.  Creating a Response with the builder pattern should not
require handling errors unless opting in by using a `try_`-method
(which returns a `Result<Builder2>`, not a `Builder2` with an interal
error state).

This is really a breaking change to `response::Builder`, but rather
than suggstig a breaking change directly, I introduce an alternative
`response::Builder2`.  If this is liked, the original `Builder` can be
deprecated, and in a later release `Builder2` can be renamed back to
`Builder`.
kaj added a commit to kaj/http that referenced this issue Jan 2, 2021
Fixes one part of hyperium#330.  This makes it possible to create an `Uri`
using the builder pattern without hanlding errors.

This is really a breaking change to `uri::Builder`, but rather than
suggesting a breaking change directly, I introduce an alternative
`uri::Builder2`. If this is accepted, the original Builder is
deprecated, and in a later release Builder2 can be renamed back to
Builder.

The new `Builder2` is generic on its contents, which starts out as the
empty type `()`.  The parts `Scheme`, `Authority` and `PathAndQuery`
can be added in any combination, and the `build` method is implemented
for valid combinations only.
kaj added a commit to kaj/http that referenced this issue Jan 16, 2021
Fixes one part of hyperium#330. Creating a `Request` with the builder pattern
should not require handling errors unless opting in by using a
`try_`-method (which returns a `Result<Builder2>`, not a `Builder2`
with an internal error state).

This is really a breaking change to `request::Builder`, but rather
than suggesting a breaking change directly, I introduce an alternative
`response::Builder2`. If this is accepted, the original
`response::Builder` is deprecated, and in a later release `Builder2`
can be renamed back to `Builder`.
@stappersg
Copy link

@kaj please pick the best from #442 #448 and #454 and close the other two.

In other words: Reduce to amount of open merge request. Please

@kaj
Copy link

kaj commented Apr 26, 2021

@kaj please pick the best from #442 #448 and #454 and close the other two.

They fix separate parts of the problem, so I think they all are needed. If one bigger PR is considered better than three smaller I can merge them to one single PR?

@stappersg
Copy link

Aim for fewer open merge requests.

kaj added a commit to kaj/http that referenced this issue Apr 28, 2021
Fixes hyperium#330.  Creating a Response with the builder pattern should not
require handling errors unless opting in by using a `try_`-method
(which returns a `Result<Builder2>`, not a `Builder2` with an interal
error state).

This is really a breaking change to `response::Builder`, but rather
than suggstig a breaking change directly, I introduce an alternative
`response::Builder2`.  If this is liked, the original `Builder` can be
deprecated, and in a later release `Builder2` can be renamed back to
`Builder`.
kaj added a commit to kaj/http that referenced this issue Apr 28, 2021
Fixes one part of hyperium#330.  This makes it possible to create an `Uri`
using the builder pattern without hanlding errors.

This is really a breaking change to `uri::Builder`, but rather than
suggesting a breaking change directly, I introduce an alternative
`uri::Builder2`. If this is accepted, the original Builder is
deprecated, and in a later release Builder2 can be renamed back to
Builder.

The new `Builder2` is generic on its contents, which starts out as the
empty type `()`.  The parts `Scheme`, `Authority` and `PathAndQuery`
can be added in any combination, and the `build` method is implemented
for valid combinations only.
kaj added a commit to kaj/http that referenced this issue Apr 28, 2021
Fixes one part of hyperium#330. Creating a `Request` with the builder pattern
should not require handling errors unless opting in by using a
`try_`-method (which returns a `Result<Builder2>`, not a `Builder2`
with an internal error state).

This is really a breaking change to `request::Builder`, but rather
than suggesting a breaking change directly, I introduce an alternative
`response::Builder2`. If this is accepted, the original
`response::Builder` is deprecated, and in a later release `Builder2`
can be renamed back to `Builder`.
kaj added a commit to kaj/http that referenced this issue Apr 28, 2021
Fixes one part of hyperium#330.  This makes it possible to create an `Uri`
using the builder pattern without hanlding errors.

This is really a breaking change to `uri::Builder`, but rather than
suggesting a breaking change directly, I introduce an alternative
`uri::Builder2`. If this is accepted, the original Builder is
deprecated, and in a later release Builder2 can be renamed back to
Builder.

The new `Builder2` is generic on its contents, which starts out as the
empty type `()`.  The parts `Scheme`, `Authority` and `PathAndQuery`
can be added in any combination, and the `build` method is implemented
for valid combinations only.
kaj added a commit to kaj/http that referenced this issue Apr 28, 2021
Fixes one part of hyperium#330. Creating a `Request` with the builder pattern
should not require handling errors unless opting in by using a
`try_`-method (which returns a `Result<Builder2>`, not a `Builder2`
with an internal error state).

This is really a breaking change to `request::Builder`, but rather
than suggesting a breaking change directly, I introduce an alternative
`response::Builder2`. If this is accepted, the original
`response::Builder` is deprecated, and in a later release `Builder2`
can be renamed back to `Builder`.
@kaj
Copy link

kaj commented Apr 28, 2021

Aim for fewer open merge requests.

Ok, #483 replaces the previous PRs.

@kaj
Copy link

kaj commented Jul 20, 2021

There's still not much discussion here. Maybe that's because there isn't that much to discuss? Is there any actual downsides to replacing the current run-time checks in the builder patterns with compile-time typestate-based checks? This is a breaking API change (even if #483 is a non-breaking first step), so it should be taken with caution. But this is a breaking API change, so we should really want it to happen as soon as possible, way before releasing a version 1.0 of the crate.

So, for the issue itself, I'm pretty sure there is nothing that should stop this. For the implementation suggested in #483 , there may or may not be problems. Please review! :-)

@lkts
Copy link

lkts commented Jul 22, 2021

I would like to see this change. I was surprised to see body on Response returning Result because i didn't see a way to handle it properly. As a result you have to resort to expect essentially removing any value of Result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
B-rfc Blocked: request for comments. More discussion would help move this along.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants