3

I want to get some instances of TypeId for the given type T which implements Sized + 'static.

use std::any::TypeId;

trait Comp: Sized + 'static { }

impl<T: 'static> Comp for T { }

struct Foo;

fn main() {
    println!("{}", TypeId::of::<Foo>());
    println!("{}", TypeId::of::<&Foo>());
    println!("{}", TypeId::of::<&mut Foo>());
}

But the results are different. Yes, I know it's perfectly normal behavior. To handle this issue, I modified this code as below.

use std::ops::Deref;

// Same codes are omitted...

fn main() {
    println!("{}", TypeId::of::<Foo>());
    println!("{}", TypeId::of::<<&Foo as Deref>::Target>());
    println!("{}", TypeId::of::<<&mut Foo as Deref>::Target>());
}

Now it prints same values. To implement this behavior for all Comp, I wrote below.

use std::any::TypeId;
use std::ops::Deref;

trait Comp: Sized + 'static {
   type Pure: Comp;
}

impl<T: 'static> Comp for T {
   type Pure = T;
}

impl<T: Deref + 'static> Comp for T {
    type Pure = <T as Deref>::Target;
}

struct Foo;

fn main() {
    println!("{}", TypeId::of::<<Foo as Comp>::Pure>());
    println!("{}", TypeId::of::<<&Foo as Comp>::Pure>());
    println!("{}", TypeId::of::<<&mut Foo as Comp>::Pure>());
}

It won't compile because I provided conflicting implementations. How can I solve this problem?

AcrylicShrimp
  • 160
  • 2
  • 9
  • 1
    What exactly do you intend to do with a `TypeId` that may or may not correspond to the type you have in hand? – trent Mar 08 '21 at 10:50
  • Does this need to operate on types only (ie "static" methods), or are you able to use a method on values instead? – Peter Hall Mar 08 '21 at 11:04
  • This seems like a textbook use for [specialization](https://github.com/rust-lang/rust/issues/31844), which sadly isn't stabilized yet (or close to being stabilized yet). – Aplet123 Mar 08 '21 at 11:38
  • Even in nightly, you can't specialize "on" a trait. Could be solved by [How can I write a method to wrap any value in my type that can be called multiple times without requiring type annotations?](/q/55455965/3650362) (with `P: Deref` instead of `Wrapper`) – trent Mar 08 '21 at 13:02
  • 1
    But I doubt this is a useful thing to do; as I asked earlier, what are you going to do with a `TypeId` if you don't know what type it belongs to? You can't treat `&T` and `T` transparently, so you can't upcast/downcast through `Any`. I guess you could index a map with it? But that seems like it's just pushing the problem up a level, since whatever value you get will be for *either* `T` or `&T` but not both. This sounds weird and like you're trying to imagine that references don't really exist. I encourage you to not do that, and look for alternative ways to do whatever you're doing. – trent Mar 08 '21 at 13:12
  • Maybe also [Is it possible to exclude reference arguments in a generic function?](https://stackoverflow.com/q/49924247/3650362) – trent Mar 08 '21 at 13:13
  • @trentcl What I'm trying to do is implementing ECS library; I have to provide something so called `system` in ECS. Systems can refer objects in the world, via `&` and/or `&mut` as it wish. So here's my approach - for each given type `T`, get a same `TypeId` over `&T` and `&mut T`. And iterate over the world with it. Components in the world are stored in `HashMap>`, I thought it will work for me. – AcrylicShrimp Mar 09 '21 at 01:54
  • But if you *know* you're iterating by `&T`, you can simply use `TypeId::of()` instead of `TypeId::of<&T>()`, right? Whereas if you have a generic `R` and you *don't know* whether it's `T`, `&T`, or `&mut T`, you can't iterate over the right type anyway because you have a collection of `T`s and you don't know how to get the `R` from them. I'm not seeing how specialization solves this problem. – trent Mar 09 '21 at 02:42

1 Answers1

1

I finally found a solution on the official community of the Rust. It can be implemented with the specialization unstable feature.

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=4c5f206ef3a7843ed57c256ee73ac58c

#![feature(specialization)]
use std::any::TypeId;
use std::ops::Deref;

trait Comp: Sized {
   type Pure: Comp;
}

impl<T> Comp for T {
   default type Pure = T;
}

impl<T: Deref> Comp for T where <T as Deref>::Target: Comp {
    type Pure = <T as Deref>::Target;
}

struct Foo;

fn main() {
    println!("{:?}", TypeId::of::<<Foo as Comp>::Pure>());
    println!("{:?}", TypeId::of::<<&Foo as Comp>::Pure>());
    println!("{:?}", TypeId::of::<<&mut Foo as Comp>::Pure>());
}
AcrylicShrimp
  • 160
  • 2
  • 9