1. 9
    1. 3

      The ?Sized syntax is a weird one-off case, and I won’t miss it. Send + Sync bounds are already special, without having their own syntax, so trait bounds can already mean whatever they need to mean.

      1. 1

        The ?Sized syntax isn’t because Sized is ‘special’ (although, as a point of interest, Send and Sync aren’t special: they can be implemented in user code with nightly Rust today). It’s there because it’s denoting that the bound is reversed, which is a fundamental semantic difference that is relevant to trait solving and changes the way the compiler reasons about the bound. Send and Sync are not reversed in this way, and so do not need special syntax.

        The ? syntax doesn’t have anything to do with the Sized trait itself, it’s just an artifact of the fact that we don’t want to be remembering to add : Sized bounds to almost every type parameter we write.

        1. 1

          Send/Sync are marker auto traits, which already makes them different from other non-auto non-marker traits. For example, they can be added to dyn Trait with +, which regular traits that allow methods don’t support. I’ve seen users confused about this specialness that allows dyn Foo + Send, but not dyn Foo + Debug.

          The ? syntax doesn’t have anything to do with the Sized trait itself […]

          I know, I’ve read the article. It doesn’t matter. Regardless of how it works and why it’s like that, the fact is that ?Trait syntax is used only for this single special case. It has nothing else to be consistent with (e.g. the other implied bound of 'static in Box isn’t using ?'static). It’s not an analogy of some other syntax in Rust (nothing to do with the try operator). Either way users have to learn about the implied bound and this trait as an exceptional case. It’s a behavior and syntax that users won’t deduce on their own. Contrast this with multiple uses of patterns and let that recycle an existing syntax for if let, let else, and let chains. Or the intuition users build around meaning of _, that many people have proposed _::Variant as an obvious syntax extension for Swift’s .Variant. Nobody looks at ?Sized and says “oh yeah, obviously it’s reversing an implied default bound”.

          The argument against dropping ? could be that users seeing T: Sized would think it’s just a regular trait bound, but to me this ship has already sailed. T: Foo + Sized + Send has already different kinds of trait bounds mixed together, so it can just be explained as some traits being special and doing whatever they’re doing. ? is needlessly generalizing a one-off case.

          1. 1

            Send/Sync are marker auto traits, which already makes them different from other non-auto non-marker traits.

            Different in a specific and not-really-relevant way, yes: but they still constrain the caller like any other trait, and that’s the thing that matters here. Send and Sync do not meaningfully differ from regular traits with respect to what they constraint (and hence where confusing compiler errors might appear). Not so with ?Sized or Unsized, which are both not trait bounds, and so laundering them as trait bounds produces confusing results.

            Either way users have to learn about the implied bound and this trait as an exceptional case.

            But that’s the case regardless, and removing the special syntax only adds to the confusion! Inverted traits have strictly different properties during trait solving, so the user needs to be able to understand them no matter what. By having special (and particularly searchable) syntax, it gives users an easy path toward learning more and understanding why this case differs from other trait bounds they’ve seen elsewhere. Removing the special syntax does not remove the specialness of Unsized, it just makes it more difficult to observe that it’s special.

            EDIT: FWIW, I think the ‘non-default’ requirement for dyn Trait can and should be removed in favour of only allowing a single ‘dispatchable’ trait in the trait object (i.e: a trait that results in a non-empty vtable), and this would further peel back the apparent specialness of Send and Sync anyway.

      2. 2

        Perhaps an unpopular opinion, but I quite like the ?Sized syntax. I think it communicates what it represents exceedingly well, and I instantly considered it a brilliant idea the moment I first understood exactly what it’s doing. Perhaps it’s because I’m a bit more steeped in compiler internals than most, but to me it’s important to note that it’s doing something special: removing an implied constraint upon the caller rather than adding a constraint to the caller. Those things are fundamentally different, and the difference is visible and important to the user, so it deserves dedicated syntax.

        1. 1

          I don’t understand when you would want a DynSized type, a type that’s size can’t even be know at runtime. Is this just for handling opaque types from C?