-
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
Expose the type_name intrinsic #1428
Comments
Probably would be good for diagnostics/panic messages in libraries. E.g. @sfackler why the Reflect bound? |
We use bounds like Thoughts, @rust-lang/libs? |
|
I would ditch the reflect bound, but I'm not actually that familiar with our current reflection strategy. If this makes it incoherent, that's bad. I am slightly terrified that people will use this to impl adhoc name-based specialization. |
You can already do that in a less messed up way by comparing |
@ticki macros don't have type information and don't work in generic contexts |
@sfackler It needs the That said, |
OTOH mem::size_of already violates parametricity |
@jonas-schievink Indeed, but in a much "lossier" way. (FWIW, I'm not terribly fond of the whole |
I don't even see anywhere in-tree where this is used except test cases. It was added in rust-lang/rust@e256b7f but even then didn't appear to be used. Please lets come up with a concrete use case and use it, not just add the feature speculatively. |
@brson I opened this issue because it was a thing I wanted to use for a concrete use case. Specifically, rust-postgres reports this error when the user attempts to convert a Postgres value to an incompatible Rust value or vice versa, for example a Postgres |
I would expect more something like // no need for Reflection, resolved at compile time, possibly make it even const
pub fn type_name<T>() -> &'static str
// resolved dynamically, hence needs reflection, no obvious need for size bound
pub fn type_name_of_val<T: Any>(val: &T) -> &'static str The former would be convenient when generating error messages as pointed out by @nagisa. pub fn downcast<'a, T: Any>(x: &'a Any) -> Result<&'a T, String> {
match x.downcast_ref() {
Some(y) => Ok(y),
None => Err(format!("got {}, expected {}", type_name_of_val(x), type_name<T>())),
}
} |
@ranma42 You can implement One distinction I think we should make is between things which exist for debugging/development purposes and things which are used for implementing "actual/intended program behavior". I think it's totally fine to pierce whatever abstraction boundaries you want in the former case, and not at all in the latter. Of course, how to prevent things intended for the former from being abused for the latter is an interesting question, but even Haskell has So that then raises the question of what |
@glaebhoerl I might be missing something (in particular, the signature I used on the function is possibly wrong), but in my mind the purpose for |
Ah. In that case you probably wanted to write |
I also have a use-case for this (entity component system: asserting that each entity with a certain component has all of that component's dependencies). I can currently print a message along the lines of "Entity {} failed to fulfill all its dependencies", which isn't the most user-friendly. |
One way to make the "only for debugging purposes" @glaebhoerl wants is to simply enforce it, by exposing a new fn like this: #[cfg(debug_assertions)]
pub fn debug_typename<T>() -> &'static str {
unsafe {
intrinsics::type_name::<T>()
}
}
#[cfg(not(debug_assertions))]
pub fn debug_typename<T>() -> &'static str {
"[no type information]"
} It doesn't need to be a macro, but it could be -- for some reason it feels better to have a macro that changes behavior at runtime (like |
@durka sadly this doesn't work quite right because cfgs are statically evaluated during compilation of the library. In this case, the standard library. So they would have to be using a custom "debug std" to see these names. Although perhaps this is what you're going for? |
@gankro meh, you're right. So that's the real reason it would be a macro :) -- that way it could expand into a macro_rules! debug_typename {
($t:ty) => {
(
#[cfg(not(debug_assertions))] "[no type information]",
#[cfg(debug_assertions)] unsafe { ::std::intrinsics::type_name::<$t>() }
).0
}
} |
I wanted to use this recently to add some instrumentation to std. Though that doesn't require stabilization I admit this is useful :) |
I found this useful for adding meaningful debug logging to a function that accepts a generic type, where I was only concerned about figuring out if it was being called for certain types. |
I'd like to add a stable wrapper on top of this, but I'm not totally sure where it should go. |
We wanted to use this intrinsic before for pass names in MIR pipeline, but it seemed to return fully qualified pathes for foreign types, where all we hoped for was just the name of type. It should either:
|
It may be overkill, but it could return a struct with the path, name, and generic arguments all split out. |
There is already a user (@mitsuhiko) commenting in this thread who wants to use type_name in an important ecosystem library (failure) but needs to conform to an interface that expects The return type of this function does not have lang implications; this is a libs team question. |
@oli-obk dtolnay's example still behaves the same on nightly today it looks like. |
But we aren't considering that here. If and when we want to make type_name a const fn, we can deal with that issue. If the fix here is straightforward, then we can fold it into the stabilization, but it does not seem to me to be a blocker. It's probably desirable to have the same names in all contexts, but every use case I'm aware of wouldn't see that as a hard blocker. |
Can someone with github permissions please remove the
I misunderstood that use case. I thought it was just expensive to use a
Yes, that's a problem for the future, and since we are explicitly stating that the exact output is unspecified, we can resolve that when the time comes.
Note that such a polymorphic function can already yield different results based on its call site by using the address of a local (debug printing a local's address is fine). Additionally I feel like having differences in debug printing inside a no_std crate and outside a no_std crate is ok. I don't think we want to end up printing paths that point into |
I understand that not every use case will be satisfied by exposing only
It does have lang implications and I disagree that it is solely a libs team question. As Felix has shown, returning
In my view it's not just about
A
It would be one thing if |
The compiler already does this, both for diagnostics directly emitted by the compiler and inserted in debuginfo. Zero people are asking for anything more precise than that output.
I do not agree that this is a bug - the different outputs are all valid names for the type. You noted above that "the reason for doing it now is that otherwise I think it will never get done." If no one cares about this enough to invest time in solving it, then why is it so important to fix that you want to prevent people from using this functionality entirely?
Good thing this is not a const fn then. |
There are a few ways forward here, I'm going to list them and list the problems mentioned with them (opinion post will follow, so please call me out if I add an opinion to thist post).
Problems mentioned with 1.
Problems mentioned with 2.
Problems mentioned with 3.
Problems mentioned with 4.
Problems mentioned with 5.
Problems mentioned with 6.
Problems with 7.
|
So... I believe that if we are talking about if we choose 5 (const fn + impl Debug) I see no reason not to go with 7 (also have const fn foo<T>() -> &'static str {
&format!("{}", type_name::<T>())
} So if we choose to keep the door open for So, if we are talking
It is used uniformly. What you see above is not two calls to Additionally, calling a random library crate may give you arbitrary output. That this output depends on the That said, we can easily "fix" the output to use absolute paths, but imo the output will be less helpful to users than if we keep the current output. This is meant for debugging, and debugging is something humans do. Let's make it easier for humans to work with. |
The context here was that it was claimed that the return type is a T-Libs question. That the compiler does this for diagnostics and for debuginfo is irrelevant. These are not part of the language's specification but with
For That a function as simple as
This discounts the other reasons for
Emphasis on at some point. The
The inverse question should be answered in my view -- why is the compilation model of linking together independent crates relevant?
I disagree that it makes things easier for humans or that it is more useful for debugging. If you are e.g. logging errors then it seems unfortunate that some errors should mention |
I don't see how this is true. The intention is this to give access to the debug information available to the compiler, not for this to be specified as part of the language with the exact format. |
What I'm saying is that users will end up being able to do these two things even with
I fully agree, my question was about without
Any sort of debugging code will have surprising results. Usage of the
How long it takes for something to become stable is not relevant to my argument. We could use
If you compile a crate on one computer as a cdylib and have it export a type name, and use it from a different crate compiled on another computer with a possibly different rustc, you don't have any guarantees for equality of the result of printing the same type. But through the cdylib (or e.g. by exchanging info via network) we may end up in a situation where both stringified versions of a type end up in a single runtime program.
Deduplication is only useful if you are automatically processing things.
I thought the argument for deterministic I'm ok with changing the output to absolute paths, especially since we're not guaranteeing output stability in any way. I just find it surprising to have a different output than what diagnostic messages are giving us. With diagnostics we are trying to give output that is close to what the user expects at the site where they used the type. |
Has anyone brought up the possibility of |
I still feel strongly that @rfcbot resolve action-plan (I would still like to see a report in brief on the stabilization PR however before it is merged) |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
From a brief discussion on Discord with @oli-obk , it should actually be trivial to make Some comments mention potential issues with making |
The final comment period, with a disposition to merge, 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 will be merged soon. |
s-soon? |
Well there's nothing to merge in this repo; it's not a PR... |
It looks like people largely agreed to make We just have to write up the actual RFC for it. With an actual RFC, that discussion can be pointed at this issue as a "pre-discussion" and should be able to go through quicker than a normal RFC. |
This does not require an RFC. I just need to get around to finishing up rust-lang/rust#60066 |
Stabilize the type_name intrinsic in core::any Stabilize `type_name` in `core::any`. Closes rust-lang/rfcs#1428 FCP completed over there. `RELEASES.md`: Prefer T-libs for categorization.
This has now been stabilized as |
We have an intrinsic which returns a
&'static str
naming a type but it's not exposed in a safe/stable manner anywhere: http://doc.rust-lang.org/std/intrinsics/fn.type_name.htmlSomething like this should suffice:
The text was updated successfully, but these errors were encountered: