-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
More implicit bounds (?Sized, ?DynSized, ?Move) #2255
Comments
I think it will make it better, because sized is right now one single exception. If there are more of it, it becomes more of a rule. |
Relevant: https://gankro.github.io/blah/linear-rust/ (I really would like more implicit bounds, however I am concerned about making things too complicated) |
Worth noting that while Sized currently is not implied in many cases (e.g. on a trait) we probanly should make things like Move get implied everywhere, and require explicit |
@Manishearth I'd expect it to be written more often as |
@mikeyhew thanks, I’ll update it after the holiday. Edit: Fixed links. |
Is there a "induction based" solution? I mean, what if
automatically implies
The (naive) idea is that wherever 'move operation' is implied in the method signature, an implicit 'where clause' requiring the type is Move is automatically added on a method basis. The existence of Move bound should automatically assume Sized and DynSized, i think... On the concrete type side, make these auto trait. So they're automatically inferred just like Send and Sync... I wonder if this can work? |
In addition to the concerns @kennytm identified in the original post, it appears that adding additional In terms of usability, the deep problem with these traits is not that they're weird or confusing, but that by their nature they make some question into everyone's problem. Making more of them does not reduce the problem, it multiplies it. Today, it is everyone's problem to know if they really need to know the size of this type at compile time. Tomorrow, we would have everyone also worry about whether they need to know the size at runtime, whether they need to be able to move it after they reference it, and whatever else anyone might propose to use these kinds of traits for. I'm pretty certain we can't add any additional traits like this, and both Fortunately, I think this has been a situation where we have a sledge hammer, and people have picked it up and swung it. That is, both |
Not all traits are born equal
You need a In fact, the main place that non-data-structure code cares about
On the other hand, the payoff of non-movable types is fairly large - easy safe self-borrowing generators! Because generators sit at the corner of complicated, high-performance, hostile-data-handling code, making them usable safely is a big safety and quality-of-life improvement, possibly even worth an ugly "data structure ecosystem split". NEEDSRESEARCHBTW, I didn't research this, but how bad is the issue with |
Moving is a big decisionActually, taking a second look, it might seem that we make That's it, you might think you could have the following mental model:
The reason this is not as perfect as it looks like is that there are actually not that much non-delegating uses of On the other hand, exposing a reference to a type and then moving it is a much rarer thing - there are many places where The "moving is a big decision" problem is a far bigger thing than worries about backwards-compatibility. It makes the mental model for anyone who is writing generic code more complex - do I want to commit to allowing On the other hand, that makes |
So, I just posted something over in internals https://internals.rust-lang.org/t/pre-erfc-lets-fix-dsts/6663. There's a lot of stuff in that post, but I had an idea as I was writing that that could help with the issue of Part of that proposal involves adding a Referent
DynAligned: Referent
DynSized: DynAligned
Aligned: DynAligned
Sized: Aligned + DynSized I've taken out So the idea is, Here is an example from the post I linked above: // `T: Referent` makes it clear that `T` is any type that can be pointed to
fn assemble<T: Referent>(data: *const (), meta: T::Meta) -> *const T; I think this makes things easier to reason about. I know @withoutboats was saying that that's not the issue here, but I have a hunch that the It's altogether a reduction in cognitive load, because you stop having to worry about which trait bounds are there that you can't see. (And then if you didn't write any of those traits, I'm not quite sure how this works with |
@mikeyhew I think (for the specific case of I'm a little confused about how However, I'm doubtful that this generalizes to any ?Trait, as opposed to working for |
|
No. I mean, theoretically The point of writing
Does that clear things up a bit, or am I just making things worse? |
I definitely understand the appeal of writing a bound like I think it's confusing only because |
The only way I see to have all types implement such a trait is use |
So we have a hierarchy:
Can we avoid the For example, This implies that the following should be illegal for unsafe code:
I'm not worried about 2, but 1 seems like a somewhat arbitrary restriction. However, the benefit would be simplifying the hierarchy: we go from four possibilities to three, at least as far as the vast majority of generic code needs to care. Perhaps we should bring that down to two, and say: even if it becomes possible to move DSTs, most generic code shouldn't bother supporting that because it's not very useful. So the recommendation would be:
edit: |
Having But
The only difference between this and what you wrote is that |
@lxrec
I hear you. If you're coming at it with the goal of removing all possible trait bounds, or to specify that But in what context would you actually want to do that? Like, when would you have a generic type argument, associated type, or trait Perhaps there are cases where you're writing generic code and you literally have no requirements about the type, you don't even need it to be behind a pointer, but I'd like to see concrete examples. Two that I know of are |
@withoutboats you say in #2255 (comment) that the general concern less weirdness than making things "everyone's problem". But isn't it the other way around? Once can always ignore |
I had an idea that might be able to help with the ergonomics of Making default bounds a first-class language feature that libraries can use, instead of a special case for only a handful of builtin traits, would hopefully make them less confusing. It would also pave the way for changing which traits are default bounds in a new |
For anyone who stumbles across this in the future: The The situation with AFAIK, there are no other suggestions for new implicit bounds with strong motivation/demand. |
Yeah i still don't fully grok all the compatibility concerns. With associated types, for example would say rather than |
I am trying to find out what the general concerns are around The issue here is rather long, is there a good summary anywhere? Replying to the concerns in the issue description:
Sure, but spelling this as
This is true no matter the syntax we use for this, so avoiding @davidtwco you mention several times in your RFC that the lang team doesn't like |
I don't know of a good write-up, I got that impression from reading all of prior art that I linked in #3729 - I got the impression that I personally don't find the proposed alternative from #3729 more confusing, it's not necessarily the most intuitive but no more or less than In the recent language team design meeting on #3729 (while it was still a draft and
With the proposals from #3729 in mind, I got the impression that |
I'm with Ralf: spelling negative bounds as positive bounds just to avoid typing a |
The best write-up I can remember off the top of my head is that withoutboats' recent blog post on Pins has a " |
That's an orthogonal problem, I think. That problem is not solved by picking a different syntax, it is a semantic issue.
The question of how to write down a type that is "not statically const Sized but still weaker-Sized" is a purely syntactic one.
|
Niko wrote up his thoughts on this here. The lang team hasn't made a decision on this matter, and personally, I'd like to see more discussion and analysis. |
The concept of implicit bound was introduced together with dynamic sized type. While having a
: Sized
bound is more specialized than no bound at all, we expect that: Sized
is much more common, and thus should be present by default. We introduced the: ?Sized
syntax in #490 to opt-out of this default bound. The syntax: ?Sized
is called "relaxed bound".Later, new RFCs and PRs tried to introduce more traits like
Sized
which the common case is "this should be implemented", e.g.?Move
bound, since most types do not contain internal references and thus can be freely moved around.?DynSized
bound, since most types have known size and alignment at runtime.?Leave
bound, since most types can bedrop
'ed.These traits themselves are often necessary at language level beyond trait bounds, e.g.
Move
is needed for immovable generators (for lack of a better solution),DynSized
is needed to reject struct tails without known alignment, andLeave
is needed to support linear type.However, the expansion of implicit bounds experienced push back from lang team due to ergonomic cost, that
?Sized
itself being a "negative feature" confuses users, adding?Move
and?DynSized
will only make the situation worse,introducing new relaxed bound means downstream packages will need to reevaluate every API to see if adding
: ?Trait
makes sense, and this needs to be done for every new relaxed bound.the necessity of
Move
andDynSized
is orthogonal to whether they need to be default.the backward-compatibility may be a lie 🍰 — Relaxing the bounds in associated type, in particular
FnOnce::Output
, means the user of the trait will get less promises, which is a breaking change:Essentially, the bounds on an associated type cannot be added (which breaks implementers) or removed (which breaks users).
So the questions are,
?Trait
and allow language to grow more similar traits, despite the stated problems above??Sized
, what else should we do regardingMove
andDynSized
?I am filing this issue here to Move the discussion from those two different PRs to a potentially more proper place, as it seems having a non-
Sized
relaxed bound itself should require more discussion around the language design.The text was updated successfully, but these errors were encountered: