I am trying to use phf to create a compile time made hashmap, but this requires that the generic type has a static lifetime. I need to compare against the output of this hashmap, but it isn't working because (in my understanding) they are no longer the same type now. Something akin to this:
fn some_func<A: SomeTrait + PartialEq, B: SomeTrait + PartialEq + 'static>(map: phf::Map<&'static str, B>, a_val: A) {
let b_val = map.get("some val").unwrap();
if a_val == b_val { // causes error here
...
}
}
From my googling on this issue it seems that in the past it likely wasn't possible to do this directly (e.g. this stackoverflow link), but I am curious to know if this has changed since then. I believe I can manually implement partialeq between the two types for now to workaround this, but I was wondering if there was a way to directly specify that these two types are in fact the same, just differing in lifetime
I'm aware, I've edited the original post to include the trait explicitly. I have a lot of trait bounds so I've enabled trait aliases, seems I've gotten a little too used to have everything under a "single trait". My apologies, I was just trying to quickly come up with an abstract example before. I still have the same problem as before though because I don't know how to specify that the two types are the same type and should only differ in lifetime
To compare A with B, A: PartialEq<B> must hold. Function bounds are asserting A: PartialEq<A> and B: PartialEq<B> though, which allows A=i32, B=String which cannot be compared to each other.
Which only means that the type must be self-contained, and not & any values outside of itself. Types don't have or "possess" lifetimes, 'static or otherwise, per se: they can only be valid and/or well defined for the duration of a given 'l. Which is generally the shortest lifetime of the reference they happen to borrow, in the case of Struct<'short, 'longer, 'static> or similar.
In your case, if I'm reading this well, what you want is as simple as:
fn some_func<T>(map: phf::Map<&'static str, T>, a_val: &T)
where T: PartialEq + 'static {
let b_val: &'static T = map.get("some val").unwrap();
if a_val == b_val { // does this still throw an error?
...
}
}
I (believe I) need owned values due to the way I'm parallelizing the work, however, I think I found a way to do what I wanted to. Completely by accident while throwing stuff at the wall and seeing what stuck, but regardless. I believe? if you attach the bounds onto a struct, then put the same type into both generic parameters, the compiler will then recognize them as the same type. As an example:
// Example with plausible trait bounds
struct Example<A: Num + Unsigned + PrimInt + PartialEq, B: Num + Unsigned + PrimInt + PartialEq + 'static> {
mapping: phf::Map<&'static str, B>,
a_val: A
}
fn some_func<T: Num + Unsigned + PrimInt + PartialEq>(input: &'static str, an_example: Example::<T,T>) {
let mapping = an_example.mapping;
let a_val = an_example.a_val;
let b_val = *mapping.get(input).unwrap();
if a_val == b_val { // This no longer throws an error
...
}
}
I'd like to take a look at compiler explorer and see what the output looks like doing this, but it seems to achieve what I was hoping for
You don't need the trait bounds on the struct, you could use
struct Example<A, B: 'static> {
mapping: phf::Map<&'static str, B>,
a_val: A
}
// `T: 'static` is implied due to the lifetime bound on `struct Example`
fn some_func<T: Num + Unsigned + PrimInt + PartialEq>(
input: &'static str,
an_example: Example::<T,T>
) {
Note that you only have one type T here -- they have to be the same including any lifetime parameters. And it has to meet a 'static bound, which I'm not sure you want.
Yeah that checks out, you're right that static becomes an implied bound. I did not know/think about that being possible behavior. I think my confusion here was at least partially coming from the editor I'm using, Zed. It's a bit misleading here, because if you hover over Example and display the type it shows:
struct Example<A, B>
where
A: Num + Unsigned + PrimInt + PartialEq,
B: Num + Unsigned + PrimInt + PartialEq + 'static,
{
mapping: Map<&'static str, B>,
a_val: A,
}
Made me think it had worked the way I'd thought it would. I probably got into this rabbit hole when I got an error about not having a static lifetime bound, and created a separate generic bound to add it to rather than adding it onto the original one
Okay, and just to be sure, you do have some type that you know does not meet a 'static bound but does satisfy your other bounds like PrimInt? I'm asking because the num trait only implements PrimInt for the, well, primitive integer types, which all meet a 'static bound, and
The intention is that all types implementing this trait behave like primitive types that are passed by value by default and behave like builtin integers.
(A static lifetime bound doesn't mean a value lives forever, so String: 'static for example. Sometimes people get confused about this.)
If you're sure, the short answer is that Rust doesn't have "generic type constructors"...
For any TypeConstructor<'a, 'b, 'c, ..> you can simply extend the signature to account for all the lifetimes involved in your "type constructor's capture". On the other hand, if you need to keep the type generic as well, I don't think there's way around it without custom traits and GAT's, as @quinedot mentioned. Link to the playground for the example above here.