Is there a keyof equivalent keyword in Rust for generic constraints?

I would like to do something equivalent to TypeScript's keyof constraint in Rust. So the Typescript constraint would look something like this. Someone said to use Hash + PartialOrd but the issue there is that it is not tied directly to the key of the hashmap being used. I'm also getting issues doing HashMap<Box<dyn Hash + PartialOrd>, V>;

EVENTMAP extends EventMap,
EVENTNAME extends keyof EVENTMAP

This seems like a xy problem
What exactly are you trying to do? What's your application?
I never seen someone use a trait object as a hashmap key, why not just use a concrete type?

1 Like

Yeah your statement is not unreasonable. I'm trying to do something like this. But the EN should be keyof HashMap. Note I'm not claiming this sample is correct, it's only the closest I could get to it.

pub async fn once<EN: Hash + PartialOrd, IV, EM: Into<HashMap<EN, IV>>>(emitter: Sender<EM>, event_name: EN) {}

What errors are you running into? specifically? the actual compile errors would be great to show us.

I wrote that snippet in the playground and it had no compilation errors.

So I guess I have a two piece question. The first is of course even without errors does that fn sig look reasonable in terms of what I was trying to do? Although as I said using Hash + PartialOrd doesn't quite guarantee that the key will be one from the same HashMap.
But the second question is that I have a parent struct called Emitter and I thought I needed to do something similar like this below but then I get the error that trait objects must include the dyn keyword, which as you know doesn't work when using two Traits together.

struct Emitter<V> {
    when: Instant,
    events: HashMap<Hash + PartialOrd, V> // using HashMap<String, V> works but then type is not same as the once function
}

Just an observation before I reply to you, the HasMap key actually needs to implement Hash + Eq, not Hash + PartialOrd.

Yeah, somewhat... function signatures can get quite messy in rust. I would probably write it like:

pub async fn once<K, V, M>(emitter: Sender<M>, event_name: K)
where
    K : Hash + Eq
    M : Into<HashMap<K, V>>
{ }

putting the trait bounds in a where clause makes it more readable.
Also, why the Into<HashMap>? why not just use a HashMap?

What do you mean by this? what are your concerns?
if you have:

fn foo<K : Hash + PartialOrd, V>(map: HashMap<K, V>, key: K) { }

you can be assured that the key param will be a key for the map. We are explicitly saying that the function takes a HashMap that maps keys of type K to values of type V, and a key of type K. it wouldn't be possible to give the function a key that couldn't be used to index the hashmap.

You'd also need to parameterize the type of the hashmap key:

struct Emitter<K : Hash + Eq, V> {
    when: Instant,
    events: HashMap<K, V>
}

in rust, you need to be explicit about what you're trying to do. traits can't be used as types, they're not like interfaces in an OOP language.
you use traits through trait bounds, whenever you're writing generic code but want to ensure the generic type has a given functionality.
In the case of the HashMap: anything that can be hashed (Hash) and can be compared (Eq) can be a key. note that a given HashMap can only have one type of key.

1 Like

Thank you for the answer. As for your question " Also, why the Into<HashMap> ? why not just use a HashMap?" I switched to Into because without it I was getting error expected trait, found struct HashMap not a trait. I guess there is a trait HashMap, like the trait Hash, but I don't know where it is.

Thanks for your help again. You made some things super clear for me!

1 Like

I switched to Into because without it I was getting error expected trait, found struct HashMap not a trait

you were probably trying to do:

pub async fn once<K, V, M>(emitter: Sender<M>, event_name: K)
where
    K : Hash + Eq
    M : HashMap<K, V>
{ }

am i correct? that's the only way I could see that error showing up
that's a syntax error, you're trying to use HashMap as a trait bound for the generic type M, which makes no sense. you should just replace M with the HashMap:

pub async fn once<K>(emitter: Sender<HashMap<K, V>>, event_name: K)
where
    K : Hash + Eq
{ }

I don't think so. The whole topic looks like a bizzare attempt to drive a car by attempting to find a way to operate propeller screw and rudder on it. It just wouldn't work.

Just look on the initial question: I would like to do something equivalent to TypeScript's keyof constraint in Rust. Just look on what keof operator does and you would quickly realize that it only works for a language which tries to add some order to the initially-dynamic language which you try to hide behind kinda-sorta-static facade.

This, of course, works for TypeScript, but is so far removed from Rust it's not even funny. It may, perhaps, work with C++ templates (because they use duck-typing even if language itself is dynamically typed) but it would make no sense in Rust.

That's mistake 6c from the well-known list and my advice to the topicstarter would be to find and read some kind of tutorial first.

It's not impossible to learn Rust the way you are doing it, but this would require insane amount of effort both from your side and also from everyone else who would try to explain why what you are trying to do doesn't work.

It's much better to read about how Rust works than to try to turn it into something entirely different.

P.S. Typescript is very decent language for what it does, but what it does is, mostly, attempt to make Javascript somewhat less painful to use. Rust doesn't rely on Javascript or any other dynamic language engine, it's not designed to interoperate with existing codebase as it's primary goal thus most design decisions which Typescript employed to be able to use Javascript libraries are just not needed (and thus not present in it). It does deal with entirely different set of limitations, though: unlike Swift Rust doesn't support polymorphic generics, this affects Rust design deeply and, essentially, makes Typescript-style approach not just unneeded, but impossible to apply!

did you read it? I meant what he was trying to do that was causing a E0404.

Agreed, rust is unlike other languages, sometimes instead of asking for "I can do this in X language, how do I do the same thing in rust", ask about the actual problem you're trying to solve.

The key to this thread was a confusion regarding rust's type system, generics, and traits.

1 Like

I wouldn't even try to guess what was the original task. The whole topic is, quite obviously, somewhat extreme variant of XY Problem. The only question is: how far removed the problem which topicstarter actually wanted to see solved from the point in the maze of bad decisions which lead to an attempt to try to seek keyof analogue in Rust.

And the only obvious part is that there were quite a few of them.

I suspect is deeper. I'm not even sure topicstarter realizes that types in Rust exist not to just detect certain bugs at compile-time, but they affect the code more deeply than by adding certain checks to generated code. Again: wouldn't even try to guess how much topicstarter knows or not knows about Rust type system. The only obvious thing is that confusion is deep, it's not just an issue with minor misunderstanding of some complex topic.

2 Likes

Yep that was it! Thanks

1 Like