I'm working on a function that takes a generic type then returns the reference of the responding field. To do this in safe rust, I have to via Any. It theoretically will cause performance overhead, but I don't know whether the compiler will help me to optimize it out.
use std::any::{Any, TypeId};
struct MyData {
a: String,
b: Vec<u8>,
c: u32,
}
impl MyData {
#[inline(always)]
fn get_raw(&self, id: TypeId) -> Option<&dyn Any> {
if id == TypeId::of::<String>() {
Some(&self.a)
} else if id == TypeId::of::<Vec<u8>>() {
Some(&self.b)
} else if id == TypeId::of::<u32>() {
Some(&self.c)
} else {
None
}
}
#[inline(always)]
fn get<T: 'static>(&self) -> Option<&T> {
self.get_raw(TypeId::of::<T>())
.map(|x| x.downcast_ref().expect("type mismatch"))
}
}
fn main() {
let my_data = MyData {
a: "hello world".into(),
b: vec![1, 2, 3, 4, 5],
c: 200,
};
// Will this be optimized into
// `println!("{}", &my_data.a);`
// ?
println!("{}", my_data.get::<String>().unwrap());
}
why are you doing this? There's most definitely a better way to do what you want, sure this is a xy problem
also, no i don't think that would get optimized since you're handling type information at runtime.
EDIT: this kind of "reflection" behaviour is really necessary, I'd do it like this: Rust Playground, not only is it waay safer and more ergonomic, I'm pretty sure it will render better optimizations
btw, this is the nightly Provider API, consumed via request_ref and request_value. It's used for generic member access where the set of types isn't necessarily known ahead of time, such as for reflection APIs or Error.
Doing so as an inherent method likely isn't actually what you want (just use multiple accessors instead), but in a generic interface it's definitely useful. (Even if it's fully monomorphized and isn't used dyn at all.)
I would say the optimizer is unlikely to see through any TypeId shenanigans, but... I'd be wrong. See this godbolt example: in retrospect it's clear that this should be fairly trivial to inline and constant propagate; after inlining it'll look something along the lines of
It definitely compiles and returns None, if you just use f64 as generic parameter; and it can't be compiled no matter the parameter used, if you pass in an argument which isn't expected by field().