-
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 non_static_type_id #41875
Comments
Implementation instructions:
|
I'd like to participate on this :) |
@z1mvader Great! Don't forget to put "r? @eddyb" in your PR when you open it. |
Is someone working on this issue? I would like to help with it. |
Hi, I'm currently working on it. I'm working on the tests |
Could These currently require the cc #35943 |
@kennytm Those sound like they do the same thing as |
@eddyb Thanks, I misread RFC. Sounds like doing this soundly will require |
Not exactly. Lifetimes are a bit... "infinite" in their multitude. What you want (like I mentioned in the RFC comments) is a scheme to specify where each of N lifetimes go (e.g. This scheme requires HKT/ATC or even messier generic traits with HRTB. The main downside is the manual impls necessary without some kind of type lambda. |
@z1mvader Are you still working on this? |
i thought @kennytm was. I no longer have time sorry :( |
@z1mvader o_O No I did not write any code on this, I was just making a comment in this thread 😅... |
I'll be working on this tomorrow, just so anyone who is watching this issue knows. |
Turns out this doesn't exactly help with my problem so I'm probably not going to be finishing my PR for this. Rustc also doesn't want to build properly on my machine, so I'd rather not waste too much time trying to fix it. |
Here is a quick workaround for anyone waiting on this to be implemented. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct TypeId {
id: usize,
}
impl TypeId {
pub fn of<T: ?Sized>() -> Self {
TypeId {
id: TypeId::of::<T> as usize,
}
}
} Edit: Better: use core::any::TypeId;
use core::marker::PhantomData;
use core::mem;
pub fn non_static_type_id<T: ?Sized>() -> TypeId {
trait NonStaticAny {
fn get_type_id(&self) -> TypeId where Self: 'static;
}
impl<T: ?Sized> NonStaticAny for PhantomData<T> {
fn get_type_id(&self) -> TypeId where Self: 'static {
TypeId::of::<T>()
}
}
let phantom_data = PhantomData::<T>;
NonStaticAny::get_type_id(unsafe {
mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>(&phantom_data)
})
} |
@dtolnay How... is that guaranteed to work, at all? Not to mention that having |
It worked great when I tried it 🤷♂️. Here is the intrinsic version. pub fn type_id<T: ?Sized>() -> u64 {
type_id::<T> as usize as u64
} |
@dtolnay I don't see how that's gonna work besides causing infinite recursion 😄 |
Note that it doesn't call, it casts a function pointer. But I expect mergefunc ( |
Seems to work with |
@dtolnay Still, there is nothing guaranteeing this won't be optimized away - if mergefunc is enabled right now, I'd even consider its failure to collapse those instances as a bug. |
@bluss why is it wrong, to compare &f64 and &'static f64, according to the RFC they will always have the same |
If I understand correctly, it would be an ok verdict from the function itself, it's just dangerous if users act on it, for example, you might assume that if two types are equal (according to that comparison), then you can freely cast a raw pointer to one to the other type, and this breaks down with types that have lifetimes. |
I think part of the problem with the conversation in this issue is we all mean slightly different things by type equality, wrong, safe and unsound. If a cast is made as a result of the erroneous belief that While this feature clearly can be a foot-gun. The solution is to document that If this feature were added to nightly with updated doc I don't think there would be very much confusion. |
@rfcbot cancel |
@joshtriplett proposal cancelled. |
@rfcbot close |
Team member @joshtriplett 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. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
It's probably worth adding a bit of context here. We discussed this in our recent @rust-lang/lang team meeting and the general feeling was that this feature just felt like it wasn't adding enough value, given the potential for confusion and misuse. |
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. |
I don't believe this was ever implemented. I have pushed an update to the RFC indicating it was retracted. I'm going to close the tracking issue now. |
…komatsakis Stabilize const_type_id feature The tracking issue for `const_type_id` points to the ill-fated rust-lang#41875. So I'm re-energizing `TypeId` shenanigans by opening this one up to see if there's anything blocking us from stabilizing the constification of type ids. Will wait for CI before pinging teams/groups. ----- This PR stabilizes the `const_type_id` feature, which allows `TypeId::of` (and the underlying unstable intrinsic) to be called in constant contexts. There are some [sanity tests](https://github.com/rust-lang/rust/blob/master/src/test/ui/consts/const-typeid-of-rpass.rs) that demonstrate its usage, but I’ve included some more below. As a simple example, you could create a constant item that contains some type ids: ```rust use std::any::TypeId; const TYPE_IDS: [TypeId; 2] = [ TypeId::of::<u32>(), TypeId::of::<i32>(), ]; assert_eq!(TypeId::of::<u32>(), TYPE_IDS[0]); ``` Type ids can also now appear in associated constants. You could create a trait that associates each type with its constant type id: ```rust trait Any where Self: 'static { const TYPE_ID: TypeId = TypeId::of::<Self>(); } impl<T: 'static> Any for T { } assert_eq!(TypeId::of::<usize>(), usize::TYPE_ID); ``` `TypeId::of` is generic, which we saw above in the way the generic `Self` argument was used. This has some implications for const evaluation. It means we can make trait impls evaluate differently depending on information that wasn't directly passed through the trait system. This violates the _parametricity_ property, which requires all instances of a generic function to behave the same way with respect to its generic parameters. That's not unique to `TypeId::of`, other generic const functions based on compiler intrinsics like `mem::align_of` can also violate parametricity. In practice Rust doesn't really have type parametricity anyway since it monomorphizes generics into concrete functions, so violating it using type ids isn’t new. As an example of how impls can behave differently, you could combine constant type ids with the `const_if_match` feature to dispatch calls based on the type id of the generic `Self`, rather than based on information about `Self` that was threaded through trait bounds. It's like a rough-and-ready form of specialization: ```rust #![feature(const_if_match)] trait Specialized where Self: 'static { // An associated constant that determines the function to call // at compile-time based on `TypeId::of::<Self>`. const CALL: fn(&Self) = { const USIZE: TypeId = TypeId::of::<usize>(); match TypeId::of::<Self>() { // Use a closure for `usize` that transmutes the generic `Self` to // a concrete `usize` and dispatches to `Self::usize`. USIZE => |x| Self::usize(unsafe { &*(x as *const Self as *const usize) }), // For other types, dispatch to the generic `Self::default`. _ => Self::default, } }; fn call(&self) { // Call the function we determined at compile-time (Self::CALL)(self) } fn default(x: &Self); fn usize(x: &usize); } // Implement our `Specialized` trait for any `Debug` type. impl<T: fmt::Debug + 'static> Specialized for T { fn default(x: &Self) { println!("default: {:?}", x); } fn usize(x: &usize) { println!("usize: {:?}", x); } } // Will print "usize: 42" Specialized::call(&42usize); // Will print "default: ()" Specialized::call(&()); ``` Type ids have some edges that this stabilization exposes to more contexts. It's possible for type ids to collide (but this is a bug). Since they can change between compiler versions, it's never valid to cast a type id to its underlying value.
I put up a pre-RFC in https://internals.rust-lang.org/t/pre-rfc-non-footgun-non-static-typeid/17079 with a counterproposal that unblocks many of the use cases that wanted non-'static TypeId, but in a way that does not open up the risks that have been discussed above in this issue. |
There was one usecase which came up early on IIRC, that wouldn't be solved by the "dynamic conservative Nevertheless, I'm extremely wary of anything allowing actual lifetimes in there, compared to only doing the (If anyone is wondering what it takes to have an |
This is a tracking issue for the RFC "1849" (rust-lang/rfcs#1849).
Steps:
The text was updated successfully, but these errors were encountered: