-
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
RFC: truly unsized types #709
Conversation
Examples where this is needed (part courtesy of @SSheldon on the forum): |
What exactly is the difference between ‘truly’ unsized types and DSTs? The only difference I can see is that DSTs use fat pointers, while ‘truly’ unsized types use thin pointers. I don’t think that’s a necessary distinction to make, because they both have the same restriction: they cannot be used without being behind a pointer. If we, for example, introduced fat pointers that have more than one word of extra data (a feature I’ve wanted a few times), they wouldn’t need extra traits for each separate size, so why should having zero bytes of extra data be any different? |
DSTs are dynamically sized types, meaning that the size of the value is known while the value exists. To reconstruct a reference to a DST from a raw pointer, one has to obtain the size. This RFC proposes to lift this restriction in cases when the size of the holistic value is not needed immediately or is unknown. |
A huge area this would help is disambiguating function pointers and functions. Basically it would be cool if There was a thread around this, but I'm afraid and I can't find it. IIRC @eddyb said it wouldn't work exactly because the unsized vs dynamically sized problem. |
@Ericson2314: you might be referring to #661. |
Hmm, that didn't have the conversation with eddyb I remember, but that's definitely one example of people wanting safer dynamic linking. Might of been a conversation about JITing, where perhaps this is even more useful (I've never heard of unlinking dynamically linked libraries). |
@Ericson2314 Is this discuss post on |
@P1start well, not exactly as I remembered, but that's probably it. Thanks! |
This is a bit of a crazy idea, but you could give DynamicSize an associated type (Ptr) which the compiler would automatically use for references to that type. This would eliminate the need for special handling of slices and trait objects within the compiler. This type would fullfill the role of a raw pointer, *T by implementing Deref, from which the compiler constructs an implicit reference type &T which enforces the correct borrow rules, but otherwise acts like the Ptr type. A &T then implicitly converts only to DynamicSize::Ptr, but not *T. The Unsized bound is equivalent to DynamicSize<Ptr = *T>, ie. a raw pointer. |
My understanding of "unsized" is that it is the result of "unsizing", which turns static type info into dynamic values. Not that I have a better name for the types that lack any size information whatsoever. |
I agree with @Diggsey that a generalization of DST metadata might be more worthwhile than adding special cases to the possible DST values. At least, if those special cases add additional syntax and semantic, like in this proposal. |
@Kimundi, what additional syntax you are referring to? The only visible changes this proposal adds are a new marker type and the Semantics do change, but I think there are two different use cases currently lumped in with
I'm not entirely sure that case 2 is a real concern, since an implementation of a generic trait parametric on an |
@mzabaluev With regard case 2: there was a PR somewhere to allow "size_of_val" and friends to work on DSTs, so the distinction between DynamicSize and !Sized is definitely needed, although there's still a bit of a grey area between the cases of "raw pointer, but can figure out the size at runtime (not necessarily in an efficient way)" vs "raw pointer, can't figure out size/type has no concept of size". |
@mzabaluev: I meant the addition of the additional marker types and traits as additional "syntax". In my opinion, there is no difference between unsized and dynamically sized - in both cases being sized refers the knowing the size at compiletime, and in both cases there is some runtime mechanism for finding it out. Whether that runtime mechanism involves storing the size directly (slices), or in form of a vtable (trait objects), or as part of the data structure pointed-at ( I'm not saying that a DST value with a thin pointer representation is not useful, I just don't think it needs to be its own "thing". |
@Kimundi I think there is a useful difference between thin/fat pointer DSTs, which is that thin pointers can be transmuted between each other, and passed to C/C++ code as a raw pointer. It's quite easy to come up with examples where the generic constraints should be "is a thin pointer" rather than "is not a DST" (for example, compatibility with void*). |
It was my understanding that this would support cases where you couldn't find out. This is needed for functions. |
@Kimundi even the types with a size that can be calculated from content may have sufficiently different performance characteristics for this operation (e.g. O(N) for C strings vs O(1) for DSTs) so genericity may not be desirable. Anyway, there doesn't seem to be a generic way to calculate the size of a DST value, so the only difference is whether a fat pointer is required to represent a reference. |
@Diggsey If I understood your proposal about |
@mzabaluev Originally, I was thinking of something like this:
However, it might make more sense like this:
So the useful bounds become:
And for any type, it's pointer type can be obtained via:
|
@Diggsey, fyi |
@SSheldon Ah, I didn't realise that - I've updated my previous post to reflect that. |
That's quite a mouthful, and I don't think current Rust allows expressions as bounds. But if bounds like that could actually be used, a convenience trait could be provided to assert it. |
Given #738 it looks like we may end up removing all of the various marker structs, in which case adding a new Perhaps we could beef up the compiler to consider types unsized such as: struct CStr {
data: c_char,
marker: Phantom<[c_char]>,
} In this case we'd basically be saying that the compiler for type analysis should consider (just a thought) |
postponing for post 1.0; cannot spend time thinking about this. (Also, RFC is thin on details, at least for a change of this size.) |
Further subdivide unsized types into dynamically-sized types, implementing
an intrinsic trait
DynamicSize
, and types of indeterminate size. Referencesfor the latter kind will be thin, while allocating slots or copying values
of such types is not possible outside unsafe code.
Rendered