However, the last pattern can become much more powerful and interesting if we associate types with string constants. The idea is to encode that the "foo" key can be used to extract an object of type Foo , and make it impossible to use it for, say, Vec<String> . To do this, we’ll need a pinch of PhantomData :

1 2 3 4 5 6 7 8 9 10 11 12 13 14 pub struct Key < T > { name : & 'static str , marker : PhantomData < T > , } impl < T > Key < T > { pub const fn new ( name : & 'static str ) -> Key < T > { Key { name , marker : PhantomData } } pub fn name ( & self ) -> & 'static str { self .name } }

Now, we can add type knowledge to the "foo" literal:

1 const FOO : Key < Foo > = Key :: new ( "foo" );

And we can take advantage of this in the get method:

1 2 3 4 5 6 7 8 9 10 11 12 impl Config { fn get < T : Deserialize > ( & self , key : Key < T > ) -> Result < T > { let json_value = self .map .get ( key .name ()) .ok_or_else (|| bail! ( "key is missing: `{}`" , key )) ? ; Ok ( T :: deserialize ( json_value ) ? ) } } ... let foo = config .get ( FOO ) ? ;