Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upRFC: Custom DSTs #2594
Conversation
ubsan
added some commits
Nov 6, 2018
This comment has been minimized.
This comment has been minimized.
|
It's likely that this RFC should be merged with some amount of #2580, specifically the stuff for mentioning VTables. |
ubsan
force-pushed the
ubsan:custom-dst
branch
from
5eee344
to
6b91428
Nov 11, 2018
This comment has been minimized.
This comment has been minimized.
|
#2255 also seems like a relevant link, since I'm not entirely sure whether those issues are settled. I think the current status of that discussion is:
This seems like a crucial (albeit not obviously crucial) piece of the argument in favor of this RFC, though I've never seen it spelled out like this before, so it seems worth asking whether I got all of that right. My only actual concern with the RFC is the same one that niko articulated last time: this doesn't appear to be a hard blocker or a game changer for anything, so do we really want to prioritize work on it in the near future? But we can always accept the RFC and not implement it for a while; this is clearly the best proposal so far and the design space seems pretty thoroughly explored at this point. To double-check: The |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Instead of declaring a type which would otherwise be In the current language the only such types are The problem there of course is not knowing what the alignment of that field should be, so maybe instead of relying entirely on Just in general it feels slightly nicer and less magical if the act of implementing a trait only causes a trait to become implemented, rather than another one to become deimplemented. (There would be some precedent for the latter w.r.t. Another question: I'm guessing the language implementation would implicitly call the provided |
This comment has been minimized.
This comment has been minimized.
|
@glaebhoerl I would argue that an Places the language calls |
This comment has been minimized.
This comment has been minimized.
|
@Ixrec technically you're right, but it's such an incredible ergonomics win, and it's been suggested so many times. It also is an incredible ergonomics win for Matrix libraries, imo, since they'll suddenly be able to use the I'd also argue it should be a blocker for |
This comment has been minimized.
This comment has been minimized.
Yeah, I'm totally on board with actually doing this. Just wanted to be sure I understood it all correctly.
Interestingly, the extern types RFC states "before this is stabilized there should be some trait bound or similar on them that prevents [ |
This comment has been minimized.
This comment has been minimized.
|
@Ixrec ah, good, I literally started writing this because someone was suggesting we panic on |
This comment has been minimized.
This comment has been minimized.
|
Sorry, by "figure this out" I meant come to an explicit consensus on whether or not panicking was the least bad solution to that problem. Now that I look closer, it seems like that discussion petered out after rust-lang/rust#43467 (comment) which explicitly suggests we make |
This comment has been minimized.
This comment has been minimized.
|
Since we have a reasonable type system solution in this RFC, I don't think panicking is a reasonable choice. |
Centril
added
T-lang
T-libs
labels
Nov 11, 2018
Centril
reviewed
Nov 11, 2018
text/0000-custom-dst.md Outdated
text/0000-custom-dst.md Outdated
| # Unresolved questions | ||
| [unresolved-questions]: #unresolved-questions | ||
|
|
||
| - Bikeshedding names. |
This comment has been minimized.
This comment has been minimized.
Centril
Nov 11, 2018
Contributor
DynamicallySized -> DynSized;
Feels distinctly more rust-y to me since we have dyn.
This comment has been minimized.
This comment has been minimized.
eddyb
Nov 11, 2018
Member
And DynSized has been used in previous (non-RFC?) proposals and discussion, so more people are already familiar with it. Not the best argument, but I was surprised to see DynamicallySized.
This comment has been minimized.
This comment has been minimized.
ubsan
Nov 11, 2018
•
Author
Contributor
I like longer names, probably the C++ programmer in me. This name's not all that important tho :P
This comment has been minimized.
This comment has been minimized.
Ixrec
Nov 11, 2018
•
Contributor
I am very mildly in favor of the longer name if this is expected to be a trait bound that relatively few users will ever need to write directly (it is, right?)
This comment has been minimized.
This comment has been minimized.
ubsan
Nov 11, 2018
Author
Contributor
Yeah - it gets implied by ?Sized. The only time you'd need to write it is for T: ?DynamicallySized, in case you want to be generic wrt extern types.
This comment has been minimized.
This comment has been minimized.
gnzlbg
Dec 7, 2018
Contributor
I prefer longer names, but DynSized feels more consistent with the rest of Rust to me and that trumps my "longer name" preference.
text/0000-custom-dst.md Outdated
Centril
reviewed
Nov 11, 2018
| - `extern type`s do not implement `DynamicallySized`, although in theory one | ||
| could choose to implement the trait for them | ||
| (that usecase is not supported by this RFC). | ||
| - `T: DynamicallySized` bounds imply a `T: ?Sized` bound. |
This comment has been minimized.
This comment has been minimized.
Centril
Nov 11, 2018
Contributor
N.B. Type: Trait is not a bound (because Trait + 'static is a bound...), it is a constraint.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Centril
Nov 11, 2018
Contributor
So a trait Foo { ... } has an implicit first parameter we call Self which is not present in Haskell; This means that when you say something like MyTrait, it has the kind bound = k1 -> constraint where k1 : type | lifetime ;. The operator : in this context can then be understood as something of kind type -> bound -> constraint. Notice that when you say Show Int => in Haskell, Show :: * -> Constraint (a "bound") but Show Int :: Constraint.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
jturner314
commented
Nov 12, 2018
|
Something like this RFC would be really nice for the However, |
This comment has been minimized.
This comment has been minimized.
|
@jturner314 That's unworkable because it would break existing code generic over references and pointers. I think what you'd want to be able to use this feature is const generics and actually parametrize those types by the number of dimensions, e.g. |
kennytm
reviewed
Nov 12, 2018
| */ | ||
| type Metadata: 'static + Copy + Send + Sync + Eq + Ord + Unpin; | ||
| fn size_of_val(&self) -> usize; |
This comment has been minimized.
This comment has been minimized.
kennytm
Nov 12, 2018
Member
What happens if these methods are implemented to return invalid values (UB?)
struct CWStr([u16; 0]);
unsafe impl DynamicallySized {
type Metadata = ();
fn size_of_val(&self) -> usize { 3 }
fn align_of_val(&self) -> usize { 3 }
}
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
gnzlbg
Dec 7, 2018
Contributor
The text should spell out exactly why DynamicallySized is unsafe to implement and which guarantees implementations have to uphold.
This comment has been minimized.
This comment has been minimized.
text/0000-custom-dst.md Outdated
text/0000-custom-dst.md Outdated
ubsan
force-pushed the
ubsan:custom-dst
branch
from
7b10237
to
fc7c158
Nov 12, 2018
This comment has been minimized.
This comment has been minimized.
jturner314
commented
Nov 12, 2018
•
1We actually use a small-vector optimization for four axes or less, but that's irrelevant for this discussion.
Yeah, that's what I was afraid of. Another possible approach that would enable this use-case is providing an owned equivalent of Out of curiosity, what's a case where the "use |
This comment has been minimized.
This comment has been minimized.
I guess the simplest case would be any code relying on the fact that copying a |
This comment has been minimized.
This comment has been minimized.
jturner314
commented
Nov 12, 2018
•
Good point. We could treats panics as aborts in this case, though. (We already do this kind of thing for I thought of another potentially breaking case – code could rely on the original and copy from Another potentially breaking case is dealing with uninitialized memory. If the type of the uninitialized memory is Yeah, I'm coming to the conclusion that a |
cramertj
added
the
A-data-types
label
Nov 12, 2018
This comment has been minimized.
This comment has been minimized.
|
@jturner314 Rust is designed with "moves and copies are literally bitwise copies always" as a core promise, as contrasted with C++. Unsafe code is free to rely on this as much as it wants to. If it's opt-in, maybe it could happen. But I also feel like the dynamic number of dimensions is less common (or will become less common with const generics). |
This comment has been minimized.
This comment has been minimized.
|
@gnzlbg Let me clarify, suppose there are m dimensions and N elements (the length), if place the dimensions separately from the elements, then for slicing we only need to allocate for those m dimensions and borrow the existing N elements. However, if these are placed together next to each other, we'll need to allocate for all m + N items. |
This comment has been minimized.
This comment has been minimized.
I think I see what you mean. What I showed is that one can, in some cases (e.g. if the number and values of dimensions don't need to change), do this without allocating at all as long as the dimensions and values are layed out appropriately. If one wants to create a view with a different run-time number and values of dimensions over an already existing slice of elements, then one has to allocate those dimensions somewhere. I do not see any major problems with the struct View<'a, T> { data: ([&'a T; 0]) }
struct ViewMetadata<'a> {
dims: &'a [usize],
len: usize,
}
unsafe impl<'a, T> DynamicallySized for View<'a T> {
type Metadata = ViewMetadata<'a>;
...
}
impl<'a, T> View<'a, T> {
fn new<'b, 'c: 'a + 'b>(ts: &'b [T], dims: &'a [usize]) -> &'c Self {
let ptr = t.as_ptr();
let meta = ViewMetadata { dims, len: ts.len() };
unsafe {
std::raw::from_raw_parts::<Self>(ptr, meta)
}
}
}
let ts: &[T] = ...;
let dims: &'a [usize] = &[...];
let v: &View<'a, T> = View::new(ts, dims); |
This comment has been minimized.
This comment has been minimized.
|
I think you want |
This comment has been minimized.
This comment has been minimized.
KrishnaSannasi
commented
Dec 27, 2018
|
@Ericson2314 I think you got it backwards, it should be |
This comment has been minimized.
This comment has been minimized.
|
Yes sorry that is what I meant. |
Centril
assigned
cramertj
Jan 3, 2019
This comment has been minimized.
This comment has been minimized.
alexreg
commented
Jan 5, 2019
•
|
I like this proposal, in essence and most of the details. A few thoughts:
|
cramertj
reviewed
Jan 8, 2019
| - `extern type`s do not implement `DynamicallySized`, although in theory one | ||
| could choose to implement the trait for them | ||
| (that usecase is not supported by this RFC). | ||
| - `DynamicallySized` is a new trait in the `Sized` hierarchy |
This comment has been minimized.
This comment has been minimized.
cramertj
Jan 8, 2019
Member
What are the tradeoffs between this and requiring that every type implement DynamicallySized? Are there examples aside from extern type that shouldn't implement DynamicallySized?
cramertj
reviewed
Jan 8, 2019
text/0000-custom-dst.md Outdated
eddyb
referenced this pull request
Jan 30, 2019
Closed
Custom Dynamically Sized Types for Rust #1524
This comment has been minimized.
This comment has been minimized.
While I believe that the claim that this can be done is technically correct, if the amount of work or expertise required to do this is too high. In practice, for many Rust users, doing this is not possible because they can't do it. One example is the standard library itself, which doesn't do this for |
This comment has been minimized.
This comment has been minimized.
thomcc
commented
Mar 10, 2019
I'm not sure "it technically could" is accurate. How could that be made to work with Rust's current feature set? |
This comment has been minimized.
This comment has been minimized.
We'd make |
This comment has been minimized.
This comment has been minimized.
velvia
commented
Mar 12, 2019
|
Hi folks, I love this proposal, especially for parsing and access to some externally defined structs which have variable size. Does the associated type |
This comment has been minimized.
This comment has been minimized.
KrishnaSannasi
commented
Mar 12, 2019
•
|
@velvia The idea would be that you split up your data from your metadata. Because your type is a DST, and you can't have for example you could define a slice like so use std::mem::PhantomData;
struct Slice<T>(PhantomData<T>);
unsafe trait DynamicallySized {
type Metadata = usize; // len
fn size_of_val(&self) -> usize {
std::mem::size_of::<T>() * metadata(self)
}
fn align_of_val(meta: Self::Metadata) -> usize {
std::mem::align_of::<T>()
}
} |
This comment has been minimized.
This comment has been minimized.
Lokathor
commented
Mar 12, 2019
|
How would a person go about actually determining how much space it takes to store a DST value. Like, a slice is a pointer an a len, so the actual size of a slice is |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
KrishnaSannasi
commented
Mar 12, 2019
•
|
@Lokathor One thing to note is that, currently, we can only create DSTs in three ways: string literals, coercion, unsafe code (with pointer casts and transmute). So you would have set aside the space already to create the underlying |
This comment has been minimized.
This comment has been minimized.
thomcc
commented
Mar 12, 2019
|
@velvia I think the idea is that you'd use |
This comment has been minimized.
This comment has been minimized.
Lokathor
commented
Mar 12, 2019
|
@Ericson2314 cool, will do. @KrishnaSannasi no perhaps you misunderstand me, I do not care about the size of the data pointed to. |
This comment has been minimized.
This comment has been minimized.
|
@Lokathor You just reserve |
This comment has been minimized.
This comment has been minimized.
Lokathor
commented
Mar 12, 2019
|
That's the pointer only. You'd then also need the metadata after it. Which you could do with something like |
This comment has been minimized.
This comment has been minimized.
thomcc
commented
Mar 12, 2019
|
@Lokathor why would it only be the pointer? |
This comment has been minimized.
This comment has been minimized.
The metadata is part of the pointer, e.g., right now |
This comment has been minimized.
This comment has been minimized.
Lokathor
commented
Mar 12, 2019
|
Oh! Well then ignore me. |
kennytm
reviewed
Mar 13, 2019
|
|
||
| ```rust | ||
| mod core::raw { | ||
| pub fn from_raw_parts<T: ?Sized>( | ||
| pub fn from_raw_parts<T: ?Contiguous>( |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
ubsan
Mar 13, 2019
•
Author
Contributor
I mean, maybe? It seems like a silly question. We've already got ?Sized, and these are the natural extension of that. There doesn't seem to be any other way of these existing without breaking a whole heck of a lot of generic code.
cramertj
reviewed
Mar 13, 2019
| height: usize, | ||
| } | ||
| unsafe impl<T> DynamicallySized for Plane<T> { |
This comment has been minimized.
This comment has been minimized.
cramertj
reviewed
Mar 13, 2019
| type Metadata = PlaneMetadata; | ||
| } | ||
| // note that `Contiguous` is not implemented for `Plane<T>`, |
This comment has been minimized.
This comment has been minimized.
cramertj
Mar 13, 2019
Member
What prevents the automatic implementation of Contiguous given that struct Plane<T> { buffer: [T; 0] } is Sized? Does implementing Pointee make the implementation of Sized go away?
ubsan commentedNov 11, 2018
•
edited by cramertj
This has been a long time coming - similar to #2580, but more general.
On the unresolved question aboutI've chosen to make this change.?Sized -> DynamicallySized, and the need for?DynamicallySized. I think this is actually a reasonable change which makesextern types act much nicer around generic functions that already exist, and fixes all the Rust code that has been written already. No longer is, for example,Box<extern-type>allowed.Note that
DynamicallySizedis a new language trait, on the level ofSizedandCopy.Rendered