But I can’t put those in my HashMap field. I thought maybe I could Borrow, but I guess not. Don’t see how to fill in the todo!() here. (playground link).
Is there a way to do this? Another path: maybe I can intern the font names and use something like a struct FontNameId(u32) in the cache keys.
Indeed, Borrow can only be used to return references to something stored in Self. You can't create a completely new Key2 instance inside of Borrow::borrow and return that, as Borrow::borrow's signature doesn't allow it.
Have you looked at Cow as a possible solution for your problem?
You may be interested in hashbrown's Equivalent trait. You just need to implement Equivalent<FontKey2> for FontKey and then you can use the hashbrown::HashMap API as you would with std::collections::HashMap.
I had not, but just looked into it because of your comment. I think that would put a lifetime parameter on my cache: HashMap<…> field, so it’s too confusing (or impossible) for me here. But good to know about that Cow type.
there's a trick to work around the limitation, the short sumamry is, you delegate your Hash implementation, both for the owned key type, and for the lookup key type, to a intermediate thingy, and both can Borrow the common intermediate type. and because Borrow<T> has a relaxed T: ?Sized bound, you can borrow as a dyn Trait.
read the details in this "tricking the hashmap" blog post:
EDIT: forgot to mention, this article was published for early version of rust in which trait objects were written without the dyn keyword. the idea is still valid in current versions, just syntax changed. keep this difference in mind and don't get confused when reading the code snippets.
Hashbrown has no advantage over HashMap here, because HashMap::{get,entry,..} already supports this through the Borrow, Hash, and Eq traits.
Afaik Cow avoids checking the variant when drreferencing, so no performance hit in Cow either. If you want both owned and borrowed keys in the same type of hashmap, then HashMap<Cow<'static,K>,V> works nicely. Also we've enough Froms for Cow so you could probably just call .into() everywhere.
The signature of Borrow::borrow makes this somewhat awkward because it wants you to return a &Borrowed from a &Self. In the general case there is no Borrowed contained within Self that you can produce a reference to.
The advantage of hashbrown is that Equivalent::equivalent only needs to return a bool, and so doesn't require you to conjure up a value out of thin air just for the purpose of an equality check.