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

RFC: Pointer metadata & VTable #2580

Merged
merged 31 commits into from
Jan 29, 2021
Merged

RFC: Pointer metadata & VTable #2580

merged 31 commits into from
Jan 29, 2021

Conversation

SimonSapin
Copy link
Contributor

@SimonSapin SimonSapin commented Oct 27, 2018

Add generic APIs that allow manipulating the metadata of fat pointers:

  • Naming the metadata’s type (as an associated type)
  • Extracting metadata from a pointer
  • Reconstructing a pointer from a data pointer and metadata
  • Representing vtables, the metadata for trait objects, as a type with some limited API

This RFC does not propose a mechanism for defining custom dynamically-sized types, but tries to stay compatible with future proposals that do.

Rendered

@SimonSapin
Copy link
Contributor Author

A previous iteration of this RFC is also visible at #2579. It was based on @gankro’s proposal https://gist.github.com/Gankro/b053cb4d1cb3bcaec070de89734720f7

text/0000-ptr-meta.md Outdated Show resolved Hide resolved
@mikeyhew
Copy link

mikeyhew commented Oct 27, 2018

First of all, thanks for opening this rfc. It's the right way to fix the raw::TraitObject API, and is a big step toward custom DST.

My only criticism is I think the Vtable type should be generic over the trait object type, as mentioned in the alternatives section. Having different Metadata types for each trait object type would help catch errors at compile time. Also, it seems like &'static would preclude us from implementing trait object types like dyn Trait1 + Trait2 using multiple vtable pointers, and i think it would be nice to avoid making that decision in this RFC.

I like the name Pointee, I think it's an improvement over Referent because it's more clear what it means.

@gnzlbg
Copy link
Contributor

gnzlbg commented Oct 27, 2018

cc @ubsan

text/0000-ptr-meta.md Outdated Show resolved Hide resolved
text/0000-ptr-meta.md Outdated Show resolved Hide resolved
text/0000-ptr-meta.md Outdated Show resolved Hide resolved
text/0000-ptr-meta.md Outdated Show resolved Hide resolved
text/0000-ptr-meta.md Outdated Show resolved Hide resolved
text/0000-ptr-meta.md Outdated Show resolved Hide resolved
///
/// [dst]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#dynamically-sized-types-dsts
#[lang = "pointee"]
pub trait Pointee {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so... I'm assuming the compiler implements

default impl<T: ?Sized> Pointee for T {
    type Metadata = &'static Vtable;
}
impl<T: Sized> Pointee for T {
    type Metadata = ();
}
impl Pointee for str {
    type Metadata = usize;
}
impl<T: Sized> Pointee for [T] {
    type Metadata = usize;
}

Which means theoretically we could make Vtable generic over T allowing the drop_in_place method to take a raw pointer with the correct pointee type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These impls would be accurate in current Rust, but what I had in mind instead was that the compiler would automatically generate impls, similar to what it does for the std::marker::Unsize trait. As far as the standard library is concerned these impls would be "magic", not based on specialization.

Regardless, yes, making VTable generic with a type parameter for the trait object type is possible.

text/0000-ptr-meta.md Outdated Show resolved Hide resolved
(Answer: they can use a different metadata type like `[&'static VTable; N]`.)

`VTable` could be made generic with a type parameter for the trait object type that it describes.
This would avoid forcing that the size, alignment, and destruction pointers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would that avoid forcing this? Can you elaborate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without a type parameter, x.size() with x: &'static VTable necessarily executes the same code for any vtable. With a type parameter, x: &'static VTable<dyn Foo> and x: &'static VTable<dyn Bar> are different types and could execute different code. (For example, do table lookup with different offsets.) However, keeping the offset of size the same within all vtables might be desirable regardless of this API.

`VTable` could be made generic with a type parameter for the trait object type that it describes.
This would avoid forcing that the size, alignment, and destruction pointers
be in the same location (offset) for every vtables.
But keeping them in the same location is probaly desirable anyway to keep code size
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing the end of a sentence?

type Metadata;
}

/// Pointers to types implementing this trait alias are
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing the end of a sentence?

@scottmcm scottmcm added T-lang Relevant to the language team, which will review and decide on the RFC. T-libs-api Relevant to the library API team, which will review and decide on the RFC. labels Oct 28, 2018
@SimonSapin
Copy link
Contributor Author

@mikeyhew I’ve very open to adding a type parameter to VTable, I’ll just wait to get some more feedback on the RFC as-is before making significant changes.

As to supporting super-fat pointers with multiple vtable pointers, as mentioned in the alternatives section I believe this design doesn’t prevent it. Types that don’t exist yet and are added to the language in the future (possibly custom DSTs) can have a different metadata type. For dyn A + B that could be [&'static VTable; 2], for example. This proposal does however force dyn C with trait C: A + B {} to keep only having a single vtable pointer.

pub unsafe fn drop_in_place(&self, data: *mut ()) { ... }
}
```

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No drawbacks section...?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I came up short trying to think of a reason not to do this at all (as opposed to doing it differently). Suggestions welcome.

and (hopefully) more compatible with future custom DSTs proposals,
this RFC resolves the question of what happens
if trait objects with super-fat pointers with multiple vtable pointers are ever added.
(Answer: they can use a different metadata type like `[&'static VTable; N]`.)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should then [&'static VTable; 1] for dyn SomeTrait be used to make that transition smoother and to fit better with const generics?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would make some sense if we were definitely gonna have super-fat pointers with multiple separate vtable pointers as fat pointer metadata. But if we don’t and end up with a different solution to upcasting, we’ll end up with a always-single-item arrays for no reason. This isn’t really the thread to get into that discussion, but my opinion is that super-fat pointer have a significant enough size cost that I’d much prefer a different solution.

Perhaps an alternative for this RFC, more neutral with respect super-fat pointers v.s. not, would be to have type Metadata = VTable<Self>; for trait objects. (See other comments about VTable’s possible type paramater.) With the pointer/reference indirection hidden away in private fields of the VTable type, this design would be compatible with having VTable<dyn A + B> contain two pointers in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, is there a use case for generic code that accepts any trait object with any number of vtable pointer but not other kinds of DSTs?

and (hopefully) more compatible with future custom DSTs proposals,
this RFC resolves the question of what happens
if trait objects with super-fat pointers with multiple vtable pointers are ever added.
(Answer: they can use a different metadata type like `[&'static VTable; N]`.)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we doing the proposals in the right order? Shouldn't we focus on dealing with dyn A + B + C, upcasting, and such things first? Also, cc #2035.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the design proposed here is compatible enough with various options for multi-traits trait objects and upcasting such that there isn’t a strong dependency, and we don’t need to block this RFC on everything else being settled.


* The name of `Pointee`. [Internals thread #6663][6663] used `Referent`.

* The location of `VTable`. Is another module more appropriate than `std::ptr`?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and should it be called Dictionary instead? ("type class dictionary")

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Big -1 to calling it Dictionary since this typically means a key-value map (example: C#, Swift, Python).

Furthermore, here in Rust the VTable is implemented as an array of function pointers, not a HashMap (unlike e.g. Python where it is really implemented as a dict), so calling it Dictionary obscures the alleged complexity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if the implementation happened to use HashMap, I’d prefer VTable since it’s more descriptive of the role of this type. (As opposed to: dictionary of what?) I believe that vtable is a well-enough established term of art.

kennytm and others added 9 commits October 28, 2018 12:23
Co-Authored-By: SimonSapin <simon.sapin@exyr.org>
Co-Authored-By: SimonSapin <simon.sapin@exyr.org>
Co-Authored-By: SimonSapin <simon.sapin@exyr.org>
Co-Authored-By: SimonSapin <simon.sapin@exyr.org>
Co-Authored-By: SimonSapin <simon.sapin@exyr.org>
Co-Authored-By: SimonSapin <simon.sapin@exyr.org>
Co-Authored-By: SimonSapin <simon.sapin@exyr.org>
Co-Authored-By: SimonSapin <simon.sapin@exyr.org>
@SimonSapin
Copy link
Contributor Author

SimonSapin commented Oct 28, 2018

Regarding making VTable generic, if it looks like this:

struct VTable<Dyn> {}

… then it could be used with any type as a parameter. What does VTable<u32> mean? If we want to restrict VTable’s parameter to only types where it makes sense (dyn SomeTrait, dyn SomeTrait + SomeAutoTrait, etc.), we’ll need a dedicated public trait:

struct VTable<Dyn> where Dyn: ?Sized + std::marker::DynTrait {}

Do we want such a trait?

@SimonSapin
Copy link
Contributor Author

SimonSapin commented Oct 28, 2018

Hmm, maybe we could get away with this?

struct VTable<Dyn> where Dyn: ?Sized + Pointee<Metadata=Self> {}

@Gankra
Copy link
Contributor

Gankra commented Oct 28, 2018

Is there any way to ensure minimal compiler time is wasted performing monomorphization on useless vtable type params? If not, I would rather the API just be less safe.

@mikeyhew
Copy link

@gankro the word "useless" sounds a little strong there... the purpose it to make sure you can't use a vtable from a *const dyn Trait to make a *const dyn SomeOtherTrait, at least not without doing something obviously hacky like transmute.

@Gankra
Copy link
Contributor

Gankra commented Oct 28, 2018

You're supposing a situation where I somehow am writing code with two vtable types floating around, in which case I have two data pointers, and nothing can stop me from swapping the data pointers, producing the exact same effect.

@matthieu-m
Copy link

@SimonSapin I created a crate https://crates.io/crates/rfc2580 packaging a slightly revised version of this RFC, and adapting the ThinBox example to match.

I like the clean Pointee trait proposed by this RFC much better than my original MetaPointee, and by using an associated type for MetaData rather than a generic argument, type inference works much better, avoiding the boiler-plate when using the into_raw_parts and from_raw_parts functions.

Also, I must congratulate you on your ThinBox code, it worked out of the box (baring adjustments to the new API).

@matthieu-m
Copy link

matthieu-m commented Jan 25, 2021

No, ThinBox needs to know align_of for the value in order to compute the offset to the data.

Actually, it doesn't. Since the size of the meta-data is known at compile-time -- based on T -- it's possible to use a middle-pointer technique instead:

  1. Create a large allocation, similar to WithMeta.
  2. Compute the middle pointer as the allocated pointer + max(sizeof(T::MetaData), align(value)).
  3. Write the value at the middle pointer.
  4. Write the meta-data just before the middle pointer.
  5. Have ThinBox memorize the middle pointer.

This simplifies the code for Deref:

  • The meta-data is at -sizeof(T::MetaData) bytes before the middle pointer.
  • The data is right at the middle pointer.

The only moment where the alignment of the value is needed again is in Drop, but then we can just use Deref to get a complete fat-pointer and use size_of_value and align_of_value.

An example implementation can be found in the examples of https://crates.io/crates/rfc2580/0.2.0 .


As a result, I once again raise the question of whether the size, align and layout functions are necessary.

Conservatively, I would advise not exposing them -- even if they can be implemented -- for the moment.

@SimonSapin
Copy link
Contributor Author

Oh that middle pointer is a really cool trick. Congrats.

I feel that fn size_for_meta<T>(meta: <T as Pointee>::Metadata) -> usize and align_for_meta intrinsics would still be useful for other cases where such tricks might not be sufficient, such as a collections storing multiple DST values in a contiguous allocation analogous to Vec<dyn Any>.

On the other hand it’s less clear to me what not having {size,align}_for_meta enables.

While I tried to make this RFC not prevent future language extensions such as custom DSTs, to be honest I somewhat expect Rust to never have custom DSTs, or any DST much more complex than already exist. Rust already spent a lot "complexity budget" on other language features, and at some point pushing the type system further starts to have diminishing returns.

@matthieu-m
Copy link

On the other hand it’s less clear to me what not having {size,align}_for_meta enables.

I am mostly concerned about the current limitation that you cannot go from &[u8] to &Debug, because &[u8] is already a DST and one cannot build a DST of DST. It seems like a limitation we should lift at some point.

At the same time, it's been 5 years already so maybe the fact that it's not been tackled means that in practice it's not too much of a problem, and we shouldn't think too much.

I feel that fn size_for_meta<T>(meta: <T as Pointee>::Metadata) -> usize and align_for_meta intrinsics would still be useful for other cases where such tricks might not be sufficient, such as a collections storing multiple DST values in a contiguous allocation analogous to Vec<dyn Any>.

I am not sure. As long as you can access the (full) object, you can use mem::size_of_val. I've already implemented a Vec (in C++), and the implementation actually used 2 vectors (in a single allocation): a vector of offsets followed by a vector of items, to be able to jump straight to the item.

I'd imagine that in Rust you would either have:

  • A vector of (metadata + offset), followed by a vector of "data" with potential padding.
  • A vector of offsets pointing to the "middle" of each item in the following vector of (metadata + data).

And in neither case you'd need to obtain the alignment from just the metadata.

I can imagine one just implementing a contiguous vector of (metadata + padding + data) where you'd need the alignment from metadata alone to find the data, but then you'd need a linear scan to get to the Nth item which isn't terribly efficient.

Then again, with most of my experience coming from C++, I'm not used to being able to separate the v-ptr from its data, so maybe I am just not imaginative enough.


In any case, thank you very much for working on this. It's necessary for all kinds of custom storage, so I'm really keen on toying with the result.

@KodrAus
Copy link
Contributor

KodrAus commented Jan 29, 2021

Huzzah! The @rust-lang/lang and @rust-lang/libs teams have decided to accept this RFC.

To track further discussion, subscribe to the tracking issue here:
rust-lang/rust#81513

@KodrAus KodrAus merged commit b39cd86 into rust-lang:master Jan 29, 2021
@SimonSapin SimonSapin deleted the ptr-meta branch January 29, 2021 12:52
use std::marker::{PhantomData, Unsize};
use std::ptr::{self, DynMetadata};

trait DynTrait<Dyn> = Pointee<Metadata=DynMetadata<Dyn>>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does Pointee come from here? Is it assumed to be a part of the prelude?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s no plan to have it in the prelude. This is a mistake in example code, but then it’s just an example.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, sorry, just figured I might as well leave a note about these nits while I was reading through the RFC :)


#[repr(C)]
struct WithMeta<T: ?Sized> {
vtable: DynMetadata,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't DynMetadata need a generic parameter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I forget to update this example when making that change.

impl<Dyn: ?Sized + DynTrait> ThinBox<Dyn> {
pub fn new_unsize<S>(value: S) -> Self where S: Unsize<Dyn> {
let vtable = ptr::metadata(&value as &Dyn);
let ptr = NonNull::from(Box::leak(Box::new(WithMeta { vtable, value }))).cast();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this use Box::into_raw instead? Box::leak produces a &, which it seems slightly weird to then pass to dealloc in Drop (which takes a *mut _).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe. Using NonNull::from avoid the unwrap with NonNull::new or the unsafe with NonNull::unchecked.

leak returns &mut though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, you're right, my bad! Given the &mut, I think it's fine.

}

// Similarly Deref
impl<Dyn: ?Sized + DynTrait> DerefMut for ThinBox<Dyn> {
Copy link
Contributor

@jonhoo jonhoo Jan 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment above refers to Deref, but this only shows DerefMut.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "similarly" comment is a handwaving this away, leaving it as an exercise to the reader. The point of example code is to show the kind of thing that could be built on top of this RFC’s proposed APIs, not to provide a solid library you can actually copy-paste and use.

Anyway, this is a merged PR. If you feel strongly enough about fixing this example code feel free to submit another PR.

Comment on lines +211 to +225
/// For statically-sized types (that implement the `Sized` traits)
/// as well as for `extern` types,
/// pointers are said to be “thin”: metadata is zero-sized and its type is `()`.
///
/// Pointers to [dynamically-sized types][dst] are said to be “fat”
/// and have non-zero-sized metadata:
///
/// * For structs whose last field is a DST, metadata is the metadata for the last field
/// * For the `str` type, metadata is the length in bytes as `usize`
/// * For slice types like `[T]`, metadata is the length in items as `usize`
/// * For trait objects like `dyn SomeTrait`, metadata is [`DynMetadata<Self>`][DynMetadata]
/// (e.g. `DynMetadata<dyn SomeTrait>`).
///
/// In the future, the Rust language may gain new kinds of types
/// that have different pointer metadata.
Copy link

@matthieu-m matthieu-m Feb 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After experimenting with custom implementations of Box, I think there is a strong case for having strongly typed meta-data for all kinds of pointers.

The pre-allocator representation of Box is:

struct Box<T: ?Sized> { ptr: NonNull<T>, }

The post-allocator representation is very similar:

struct Box<T: ?Sized, A: Allocator = Global> {
    allocator: A,
    ptr: NonNull<T>,
}

Both automatically implements CoerceUnsized<Box<U>> where T: Unsize<U>, and all is well.

If one wants to make Box generic over its storage, then the representation becomes:

pub struct RawBox<T: ?Sized + Pointee, S: SingleElementStorage> {
    storage: S,
    handle: S::Handle<T>,
}

If S::Handle<T> == NonNull<T>, then Box is still coercible; however, in the case of inline storage, that is:

  • neither possible: when the Box is moved, so is the storage, and therefore any pointer into the storage is invalidated.
  • nor desirable: in the case of inline storage, the pointer is redundant, wasting 8 bytes.

Hence, in the case of inline storage, S::Handle<T> is best defined as <T as Pointee>::Metadata.

In order to have Box<T> : CoerceUnsized<Box<U>> where T: Unsize<U>:

  • We need: S::Handle<T>: CoerceUnsized<S::Handle<U>> where T: Unsize<U>,
  • Which means: <T as Pointee>::Metadata: CoerceUnsized<<U as Pointee>::Metadata>> where T: Unsize<U>.

And of course, Box being coercible is very much desirable.


As a result, I believe a slight change of course is necessary:

  1. All metadata should be strongly typed -- be it Metadata<dyn Debug>, Metadata<[u8]> or Metadata<[u8; 3]> -- no more () or usize.
  2. The compiler should automatically implement Metadata<T>: CoerceUnsized<Metadata<U>> where T: Unsize<U>.

I would note that having a single Metadata<T> rather than SizedMetadata<T>, SliceMetadata<[T]>, DynMetadata<dyn T> is not necessary, only the coercion is, and since the compiler is generating those, it's perfectly free to create them "cross type".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is merged. Consider sending further discussion to the tracking issue: rust-lang/rust#81513

(That said, I don’t see a point in having a Box generic over storage. Especially Box<T, InlineStorage>, isn’t it pretty much the same as T?)

SimonSapin added a commit to SimonSapin/rust that referenced this pull request Feb 15, 2021
bors added a commit to rust-lang-ci/rust that referenced this pull request Feb 18, 2021
Implement RFC 2580: Pointer metadata & VTable

RFC: rust-lang/rfcs#2580

~~Before merging this PR:~~

* [x] Wait for the end of the RFC’s [FCP to merge](rust-lang/rfcs#2580 (comment)).
* [x] Open a tracking issue: rust-lang#81513
* [x] Update `#[unstable]` attributes in the PR with the tracking issue number

----

This PR extends the language with a new lang item for the `Pointee` trait which is special-cased in trait resolution to implement it for all types. Even in generic contexts, parameters can be assumed to implement it without a corresponding bound.

For this I mostly imitated what the compiler was already doing for the `DiscriminantKind` trait. I’m very unfamiliar with compiler internals, so careful review is appreciated.

This PR also extends the standard library with new unstable APIs in `core::ptr` and `std::ptr`:

```rust
pub trait Pointee {
    /// One of `()`, `usize`, or `DynMetadata<dyn SomeTrait>`
    type Metadata: Copy + Send + Sync + Ord + Hash + Unpin;
}

pub trait Thin = Pointee<Metadata = ()>;

pub const fn metadata<T: ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata {}

pub const fn from_raw_parts<T: ?Sized>(*const (), <T as Pointee>::Metadata) -> *const T {}
pub const fn from_raw_parts_mut<T: ?Sized>(*mut (),<T as Pointee>::Metadata) -> *mut T {}

impl<T: ?Sized> NonNull<T> {
    pub const fn from_raw_parts(NonNull<()>, <T as Pointee>::Metadata) -> NonNull<T> {}

    /// Convenience for `(ptr.cast(), metadata(ptr))`
    pub const fn to_raw_parts(self) -> (NonNull<()>, <T as Pointee>::Metadata) {}
}

impl<T: ?Sized> *const T {
    pub const fn to_raw_parts(self) -> (*const (), <T as Pointee>::Metadata) {}
}

impl<T: ?Sized> *mut T {
    pub const fn to_raw_parts(self) -> (*mut (), <T as Pointee>::Metadata) {}
}

/// `<dyn SomeTrait as Pointee>::Metadata == DynMetadata<dyn SomeTrait>`
pub struct DynMetadata<Dyn: ?Sized> {
    // Private pointer to vtable
}

impl<Dyn: ?Sized> DynMetadata<Dyn> {
    pub fn size_of(self) -> usize {}
    pub fn align_of(self) -> usize {}
    pub fn layout(self) -> crate::alloc::Layout {}
}

unsafe impl<Dyn: ?Sized> Send for DynMetadata<Dyn> {}
unsafe impl<Dyn: ?Sized> Sync for DynMetadata<Dyn> {}
impl<Dyn: ?Sized> Debug for DynMetadata<Dyn> {}
impl<Dyn: ?Sized> Unpin for DynMetadata<Dyn> {}
impl<Dyn: ?Sized> Copy for DynMetadata<Dyn> {}
impl<Dyn: ?Sized> Clone for DynMetadata<Dyn> {}
impl<Dyn: ?Sized> Eq for DynMetadata<Dyn> {}
impl<Dyn: ?Sized> PartialEq for DynMetadata<Dyn> {}
impl<Dyn: ?Sized> Ord for DynMetadata<Dyn> {}
impl<Dyn: ?Sized> PartialOrd for DynMetadata<Dyn> {}
impl<Dyn: ?Sized> Hash for DynMetadata<Dyn> {}
```

API differences from the RFC, in areas noted as unresolved questions in the RFC:

* Module-level functions instead of associated `from_raw_parts` functions on `*const T` and `*mut T`, following the precedent of `null`, `slice_from_raw_parts`, etc.
* Added `to_raw_parts`
@python-ast-person
Copy link

Is fn size_for_meta<T>(meta: <T as Pointee>::Metadata) -> usize still going to be implemented? Being able to work out the size of a type without needing to have a valid reference/pointer to it would be very helpful for my use-case.

@python-ast-person
Copy link

Are there plans for fn size_for_meta<T>(meta: <T as Pointee>::Metadata) -> usize to be implemented? As far as I know, I need it for my use-case.

@programmerjake
Copy link
Member

Is fn size_for_meta<T>(meta: <T as Pointee>::Metadata) -> usize still going to be implemented? Being able to work out the size of a type without needing to have a valid reference/pointer to it would be very helpful for my use-case.

if Rust ever supports C++ virtual classes (imho something that would be useful for FFI), that won't work since the size of the type is indirectly stored in the pointee, the metadata would be a ZST.

@python-ast-person
Copy link

python-ast-person commented Feb 23, 2023

if Rust ever supports C++ virtual classes (imho something that would be useful for FFI), that won't work since the size of the type is indirectly stored in the pointee, the metadata would be a ZST.

Unless ABI compatiblity is needed, wouldn't the type pointer be stored in the metadata, as is done for dyn Trait?

@programmerjake
Copy link
Member

if Rust ever supports C++ virtual classes (imho something that would be useful for FFI), that won't work since the size of the type is indirectly stored in the pointee, the metadata would be a ZST.

Unless ABI compatiblity is needed, wouldn't the type pointer be stored in the metadata, as is done for dyn Trait?

almost the whole point of Rust supporting C++ virtual classes is ABI compatibility -- needed for FFI, so &ACppClass would have to be a pointer with ZST metadata and would have to match C++ * ACppClass's ABI

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-dst Proposals re. DSTs A-repr #[repr(...)] related proposals & ideas A-trait-object Proposals relating to trait objects. A-traits Trait system related proposals & ideas A-traits-libstd Standard library trait related proposals & ideas A-types-libstd Proposals & ideas introducing new types to the standard library. disposition-merge This RFC is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this RFC. Libs-Tracked Libs issues that are tracked on the team's project board. T-lang Relevant to the language team, which will review and decide on the RFC. T-libs-api Relevant to the library API team, which will review and decide on the RFC. to-announce
Projects
None yet
Development

Successfully merging this pull request may close these issues.