-
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
Generic Pointer to Field #2708
Generic Pointer to Field #2708
Conversation
added PinProjectable to unresolved questions
This RFC had some previous discussion here on internals I would like to focus the discussion first on if we actually need this feature in the first place. |
I wrote this crate which seems to do pretty much the same thing: https://crates.io/crates/field-offset |
Cool, unfortunately according to the current unsafe guidelines your crate is unsound to use on any type that contains references or I hadn't thought about chaining field offsets, but that doesn't seem to be mission critical, so we can decide on that later. What do you think of this RFC? |
The crate was written before the existence of the unsafe code guidelines when this was considered a valid pattern (the variable is never accessed). In practice, this does not cause any problems, but it will be nice to be able to stop relying on implementation details of the compiler once MaybeUninit is stabilised.
One of the considerations for whether something should be added as a language feature, particularly a complex and niche one such as this, is whether it can be implemented as a library (either as part of the standard library or a separate crate). It would be nice to highlight in the RFC what the benefits are of having this be a language feature as opposed to a library, and why they justify the additional complexity in the language. The motivation section currently says:
But this is greatly overstating the impossibility of implementing this in a library. |
Ok, I just noticed that this crate was last updated 3 years ago.
MaybeUninit won't solve this because the only qay to get a member of a type is to have a value, reference or box, all of which need to be a valid instance of the type to work.
This feature isn't particularly complex to implement, although it does have some far reaching implications. Some of these implications were analyzed on internals, but I suspect that there is more that we may have missed. The only parts that absolutely have to be implemented in the compiler is the
How would you get the necessary offsets of fields soundly in light of the current unsafe guidelines? I don't see how. Keep in mind that the compiler already has all the information available, this is a way of exposing that information safely. |
You can do it through a raw pointer. |
@comex How? You can't access the field through a raw pointer without already knowing its offset. If you can we can close this and implement the whole proposal as a library. But seeing as all libraries that try to provide this use UB via null pointer dereference, or References: using null pointer: crate and link to offending code I searched through hundreds of crates on crates.io to find these examples. |
If the core issue here is that you need the offset of a field in order to implement something like this as a library, then maybe this should be a As someone who has used more than a little C++, the need for pointers to members comes up rarely if ever,. Most of the time it can be done another way (i.e. there's a trick for some trees where instead of having If there's going to be a syntax to refer to a field descriptor, the next obvious question from the perspective of someone new to Rust finding this seems to me to be "how do I refer to a field's type?" That might be quite useful in macro land, and something worth doing; reserving I feel like motivation and guide-level explanation need to be fleshed out more in the direction of someone wanting to learn what the feature is. Guide-level explanation seems to be reference-level explanation, and motivation is written such that you have to already know why you want the feature in order to understand it (that's not quite right; finding better wording is failing me). But I do like what this is getting at. |
Using the
Yes, they need to be fleshed out more and reworded in general. |
An idea just came to me. We already have projection though references via patterns so we could extend that to pointers. struct Foo { a: u32, b: i16, c: f64, }
// works today
fn by_ref(Foo { b, .. }: &Foo) -> &i16 { b }
fn by_ptr(Foo { b, .. }: *mut Foo) -> *mut i16 { b } A safe offset_of as the pointer is explicitly allow to not point at a valid object. macro_rules! offset_of {
($t:path => $f:tt) => {
match core::ptr::null<$t>() {
$t { $f: a, .. } => a as usize
}
}
} |
Related issue - #1287 (about the It conflicts with associated values indeed, but |
In this proposal it's being used on a type, not a value. So it's not |
@Pauan |
@petrochenkov Ah, sorry, I misunderstood. Thanks for the correction. |
@petrochenkov @spunit262 syntax discussions are off topic for now, I would like to focus on if this feature is needed, and is it sound. If either answer to no, then this proposal may end up scraped, so syntax discussions now are not productive yet. I used the dot syntax as a placeholder for whatever syntax we end up with. |
Rust has been moving in the direction of making |
@eaglgenes101 I think the more interesting thing about unsafe code here is that having unsafe code in std, or a compiler intrinsic, or whatever means that it's by trusted people by most definitions of trusted, and enables the ecosystem to be 100% safe code for some of these use cases. |
The bare minimum proposal that we can strip this RFC down to is this /// Opaque type that can only be constructed by the compiler.
struct FieldDescriptor<F: Field> { ... }
/// Represents a field on some type, must be implemented by the compiler, and only the compiler
unsafe trait Field {
/// The type that the fields belongs to
type Parent: ?Sized;
/// The type of the field itself
type Type: ?Sized;
/// Describes the data needed to convert from *[const|mut] Self::Parent to *[const|mut] Self::Type
const FIELD_DESCRIPTOR: FieldDescriptor<Self>;
}
impl<T: ?Sized> *const T {
fn project<F: Field<Parent = T>>(self, field: F) -> *const F::Type {
// safe projection, where invalid pointers are handled in a safe, but implementation defined way
}
unsafe fn project_unchecked<F: Field<Parent = T>>(self, field: F) -> *const F::Type {
// unsafe projection, where using invalid pointers are UB
}
}
impl<T: ?Sized> *mut T {
fn project<F: Field<Parent = T>>(self, field: F) -> *mut F::Type {
// safe projection, where invalid pointers are handled in a safe, but implementation defined way
}
unsafe fn project_unchecked<F: Field<Parent = T>>(self, field: F) -> *mut F::Type {
// unsafe projection, where using invalid pointers are UB
}
} Everything else could be punted to a separate library, i.e. everything related to I find this to be just the right size for such a niche feature, only a handful of associated functions, a type, and a trait. |
as it controls unsafe code, `Field` must be an unsafe trait
@KrishnaSannasi
pin-utils::{unsafe_pinned, unsafe_unpinned} is not safe... My link is about pin-project.
Yeah, but I think GAT is needed to handle references generically.
Yeah, but if referring to each field, you need to call I think the pin-project's approach is preferred at least until these issues are addressed. |
@ckaran the raw reference operator is being fast tracked to stabilization because of how critical it is, but I'm not sure when it will land on nightly.
sorry, typo on my part.
Nope, we can do this without GAT, I'll post a minimal crate later today. See that for details.
This is the sort of stuff that I want to experiment with in a crate before putting it in std. |
@KrishnaSannasi
Looking forward to seeing it! 👍 |
@KrishnaSannasi I plan on looking at it, but will likely be slow; my PhD is taking up a lot of my time right now. |
This RFC would be a lot stronger if it was building on top of a proper compile-time reflection API. |
With a compile time reflection API and |
@KrishnaSannasi Do you mind if I make some formatting changes to your crate? It's a little easier for me to read code comments if the comments wrap at 80 characters, but I'm not sure if you're interested in that. |
@ckaran, sure just send in a PR and I'll merge it. |
We discussed this in a lang team backlog grooming meeting today. That ended up in a place that was similar to this comment from @RustyYato:
More specifically, we thought it would be good for this to follow a similar process to what safe-transmute did: see how far this can get while entirely in a library, then specify exactly what the minimal lang change requested to make this work nicely would be, with the rest of it up to libs to decide how it should look. |
(For extra clarity: from a lang perspective we'd postpone this, but this is also tagged for @rust-lang/libs so I don't want to do that unilaterally.) |
Hmm, well, no comments from libs so I'm going to move to @rfcbot fcp close The lang team discussion on this was that we're not ready to take the lang parts of this. We'd like to see how far this can get as a library solution (perhaps based around the raw pointer macros internally) and then might be open to a focused lang change to address a particularly-troublesome part. |
Team member @scottmcm has proposed to close this. The next step is review by the rest of the tagged team members:
No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
Approving on behalf of withoutboats, as per rust-lang/team#526. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
The final comment period, with a disposition to close, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. The RFC is now closed. |
Rendered
This RFC aims to provide a generic way to talk about the fields on types! Then go one step further and allow smart pointers to project to those fields!
here is a very alpha crate that implements some of the ideas laid on in this RFC.