#Run different code if generic type is a specific type?

8 messages · Page 1 of 1 (latest)

uncut viper
#

I have a trait function that I can't control that looks roughly like

fn do_something_with_s<S>(&self, s: S) where S: Trait;

I want to implement this as

fn do_something_with_s<S>(&self, s: S) where S: Trait {
    if let Some(my_custom_type) = as_my_custom_type(&s) {
        my_custom_type.do_specialized_thing()
    } else {
        s.do_trait_thing()
    }
}

How can I implement as_my_custom_type?

fn as_my_custom_type<S>(s: &S) -> Option<&MyCustomType> {
    todo!()
}

I was hoping to use something in std::any to make this work, like

fn as_my_custom_type(s: &dyn std::any::Any) -> Option<&MyCustomType> {
    s.downcast_ref::<MyCustomType>()
}

or

fn as_my_custom_type(s: &S) -> Option<&MyCustomType> {
    if std::any::TypeId::of::<S>() == std::any::TypeId::of::<MyCustomType>() {
        let mine = unsafe { std::mem::transmute::<&S, &MyCustomType>() };
        Some(mine)
    } else {
        None
    }
}

but Any and TypeId require the type parameter to be 'static and the trait I don't control does not make that guarantee.

Certainly the compiler knows the type of S; is there any way I can get it to tell me if that S is MyCustomType in specific?

Right now, I'm doing a huge hack and I don't like it, because it's not guaranteed that it will work.

#[repr(C)]
struct MyCustomType {
    magic: usize,
}

impl MyCustomType {
    const MAGIC: usize = 0x0123456789abcdef;
}

impl Default for MyCustomType {
    fn default() -> Self {
        Self { magic: Self::MAGIC }
    }
}

fn as_my_custom_type(s: &S) -> Option<&MyCustomType> {
    if std::mem::size_of::<S>() == std::mem::size_of::<MyCustomType>() {
        let mine = unsafe { std::mem::transmute::<&S, &MyCustomType>() };
        if mine.magic == MyCustomType::MAGIC {
            Some(mine)
        } else {
            None
        }
    } else {
        None
    }
}
mighty hare
#

Couldn't you simply put as_my_custom_type as a method of Trait ?

#

You could write a default impl that returns None so that you only have to write it when implementing Trait for MyCustomType

uncut viper
#

The actual use case here
I maintain the serde_dynamo crate that serializes to the AWS DynamoDB format.

DynamoDB has multiple types of lists. There's the generic "List". Then there's the specific "StringSet" which is a specialized collection type. I want to provide a StringSet wrapper struct that, when serialized, causes the collection to be serialized as a "StringSet" instead of a generic "List". For all other serializers, it just serializes what's inside of it regularly.

Ultimately it would make something like the following possible.

#[derive(Serialize)]
struct MyThing {
    #[serde(with = "serde_dynamo::string_set")]
    data: BTreeSet<String>,
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f9903469aa56a3127fbaeb960214f726

uncut viper
mighty hare
#

Do you control the bound on the generic type ?

uncut viper