-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Tracking Issue for thin_box
#92791
Comments
Example PR using ThinBox as a substitute for |
Add new ThinBox type for 1 stack pointer wide heap allocated trait objects **Zulip Thread**: https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/ThinBox Based on https://github.com/matthieu-m/rfc2580/blob/b58d1d3cba0d4b5e859d3617ea2d0943aaa31329/examples/thin.rs Tracking Issue: rust-lang#92791 Usage Trial: https://github.com/yaahc/pgx/pull/1/files ## TODO - [x] make sure to test with #[repr(align(1024))] structs etc
One unfortunate thing about ThinBox is that it's invariant rather than covariant, like Box. For example, this playground fails to compile: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=f6348153d6555716ce86926ac1928a4e. This happens because of the |
A use case that's slightly more realistic for ThinBox is https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=e2659853940505c62b73b21deb079195: #![feature(thin_box)]
use std::boxed::ThinBox;
pub trait Foo {}
pub fn add_foo(foos: &mut Vec<ThinBox<dyn Foo + '_>>, foo: ThinBox<dyn Foo + 'static>) {
foos.push(foo);
} This fails with a lifetime error, because |
FWIW, I also noted the invariance of |
|
Oh, it should definitely be Send and Sync. |
Implement `Send` and `Sync` for `ThinBox<T>` Just like `Box<T>`, `ThinBox<T>` owns its data on the heap, so it should implement `Send` and `Sync` when `T` does. This extends tracking issue rust-lang#92791.
Implement `Send` and `Sync` for `ThinBox<T>` Just like `Box<T>`, `ThinBox<T>` owns its data on the heap, so it should implement `Send` and `Sync` when `T` does. This extends tracking issue rust-lang#92791.
Make `ThinBox<T>` covariant in `T` Just like `Box<T>`, we want `ThinBox<T>` to be covariant in `T`, but the projection in `WithHeader<<T as Pointee>::Metadata>` was making it invariant. This is now hidden as `WithOpaqueHeader`, which we type-cast whenever the real `WithHeader<H>` type is needed. Fixes the problem noted in <rust-lang#92791 (comment)>.
Is it not supposed to implement Eq? because Box does. |
I was wondering, Because I get this error:
|
|
We should think at least about the fundamental traits before stabilization -- Maybe |
Another auto-trait consideration is rust/library/alloc/src/boxed.rs Lines 2017 to 2041 in fb88811
|
I always thought that Unpin was automatically implemented on all types. Does unconditional simply mean you can't opt out of Unpin? |
The unconditional impl on |
Make `ThinBox<T>` covariant in `T` Just like `Box<T>`, we want `ThinBox<T>` to be covariant in `T`, but the projection in `WithHeader<<T as Pointee>::Metadata>` was making it invariant. This is now hidden as `WithOpaqueHeader`, which we type-cast whenever the real `WithHeader<H>` type is needed. Fixes the problem noted in <rust-lang/rust#92791 (comment)>.
As a side question, is there any plan for a similar |
@cuviper The pointer to the
|
@dead-claudia It can be done safely, but there's a trade-off. Referring to the code comment that I linked, we can either have unconditional
So the question is whether |
@cuviper Could you explain the difference between the two? I'd think that |
I still get confused about pinning myself, but hopefully this section of the docs will help you: That said, I think we probably do want unconditional
|
So, I'm wondering if there would be some way to recontextualize this in terms of an unsized struct instead of a thin box as-is. In other words, whether it would be possible to make My justification for this is for use cases such as I was also thinking of this from the perspective of creating an unsized_vec type data structure, except for storing trees inside a single buffer. If this seems even remotely doable, I'm willing to sketch out a potential API for it and maybe a PR for it, but I don't know 100% what the reasoning for specifically going for a |
My original motivation for working on this was use cases in error handling. I wanted to have the same thin pointer benefits that In the end I gave up on it because I didn't think the UX would be adequate to justify all the extra types people would have to juggle and think about, especially with the whole thing requiring a second |
Makes sense! I have other stuff I want to get to first, so, I don't mind if someone ends up getting to this before me. Going to just dump my thoughts here on what I was thinking of for a design, although I haven't actually hashed it out much. The main thing that came to mind for this is that we'd effectively end up having to resolve the DST issues that are preventing us from making I was thinking that it might be useful to have effectively three versions of the thin wrapper: one that's equivalent to the current implementation, but another where the metadata is stored after the value, and one where the metadata is stored redundantly on both sides. The main reasoning for this is that the "unsized slice" case where you're storing multiple of these objects in one buffer might want the metadata at the end to allow backwards iteration. One particular case I was looking into is the ability to have a "type-safe" version of a tree storable in a buffer, primarily for parsing prefix/postfix arithmetic expressions. For prefix expressions, you'd have the metadata at the beginning of operands, whereas for postfix expressions, it would be at the end. And a more general "unsized vec" might just have them on both sides to allow easy double-sided iteration. For the case where metadata is only at the end, it's an example of a DST whose size isn't knowable from a pointer alone. If you know the size of it already, you can find the metadata and reconstruct a fat pointer, but you need the size or a pointer past the end of it in order to do that. If it's at the beginning, you can just read the metadata to determine the layout. |
Just chiming in here since this came up as a potential solution to a problem but it seems there's no real way to create a Can we get a |
Presumably rust-lang/rfcs#3536 will help provide a solid path for this. |
What is the difference between this and a normal |
A A This would make it easier to pass pointers to unsized types through FFI boundries. Example of
|
Is it guaranteed what the layout of Metadata will be? Would it be feasible to make a ThinBox FFI-safe? If we can guarantee:
a |
I was under the impression this is exactly the kind of thing we didn't want to have, since we want a path forward for types which are unsized but not length-prefixed, like |
In that case, perhaps it could be made optional somehow - the problem there would be how we make the length optional. It's valid to have a slice with a length of 0, and a pointer that's not NULL. You can also make a |
Worth noting that, for fat empty slices, the slice pointer is in practice still never null, to ensure that Not that you should ever depend on this for correctness, mind you. This is just the current implementation to my knowledge. |
You can depend on it being non-zero because Empty boxes might be able to optimise differently since you generally don't want to reference a valid allocation if it's empty, although there are also other cases that depend on pointer-equality being different even for empty allocations. I would just generally not rely on pointer checking to determine size. The main case of a no-metadata DST is CStr, which is never an empty allocation; an empty string is |
Create try_new function for ThinBox The `allocator_api` feature has proven very useful in my work in the FreeBSD kernel. I've found a few places where a `ThinBox` rust-lang#92791 would be useful, but it must be able to be fallibly allocated for it to be used in the kernel. This PR proposes a change to add such a constructor for ThinBox. ACP: rust-lang/libs-team#213
Rollup merge of rust-lang#110483 - tleibert:thin-box-try-new, r=dtolnay Create try_new function for ThinBox The `allocator_api` feature has proven very useful in my work in the FreeBSD kernel. I've found a few places where a `ThinBox` rust-lang#92791 would be useful, but it must be able to be fallibly allocated for it to be used in the kernel. This PR proposes a change to add such a constructor for ThinBox. ACP: rust-lang/libs-team#213
More ways of making ThinBox slices would likely be helpful - e.g.:
|
Now that I'm thinking about this more, I'm starting to like the basic idea in #92791 (comment) more and more. Here's my intuition:
I do feel it can't be fully done in userland, though, and it feels more like a |
Feature gate:
#![feature(thin_box)]
This is a tracking issue for
ThinBox
for making heap allocated DSTs which only occupy 1 pointer on the stack.Public API
Note: This API is expected to grow to match
Box
as necessary over time. The initial API is the minimum needed for basic usage for error handling.Steps / History
Send
andSync
: ImplementSend
andSync
forThinBox<T>
#98595Unresolved Questions
ThinBox<T>
covariant inT
#98585const_allocate
fleshed out and tested enough to justify it being used in thinbox's impl? Does T-lang agree we'll always have some way to procedurally generate allocations in const eval? (in contrast to declaratively via constants, statics or anonymous promoteds)The text was updated successfully, but these errors were encountered: