rust - Weird macro behavior for type inference -
i'm trying understand macros & utilizing simple macro create hashmap similar vec!
. reason if convert string
&str
inside macro doesn't seem work.
macro_rules! hashmap { ($( $key: expr => $val: expr ),*) => {{ let mut map = ::std::collections::hashmap::new(); $( map.insert($key.clone(), $val.clone()); )* map }} } fn works(){ let value1 = 5; let value1str: &str = &value1.to_string(); let h = hashmap!["key0" => "value0" , "key1" => value1str]; println!("{:?}", h); } // fn does_not_work(){ // let value1 = 5; // let h = hashmap!["key0" => "value0" // , "key1" => &value1.to_string()]; // println!("{:?}", h); // } fn main(){ works(); //does_not_work(); }
this effective body of works()
function:
fn works(){ let value1 = 5; let value1str: &str = &value1.to_string(); let h = { let mut map = hashmap::new(); map.insert("key0".clone(), "value0".clone()); map.insert("key1".clone(), value1str.clone()); map }; println!("{:?}", h); }
and 1 of does_not_work()
:
fn does_not_work(){ let value1 = 5; let h = { let mut map = hashmap::new(); map.insert("key0".clone(), "value0".clone()); map.insert("key1".clone(), (&value1.to_string()).clone()); map }; println!("{:?}", h); }
when clone()
called on &str
, simple byte-wise copy of fat pointer (two pointer-sized numbers): shared references copy
; cloned reference has same lifetime original one, naturally. okay in works()
because 3 of string slices used there point static memory string literals, , fourth 1 points temporary local variable lives until end of function:
let value1str: &str = &value1.to_string(); // in fact equivalent let temporary = value1.to_string(); let value1str: &str = &temporary;
because temporary
lives longer hash map, reference can stored inside hash map: map gets destroyed first , never gets chance hold dangling reference.
in second case, however, clone()
called on value of type &string
, because &s
if s: string
give &str
if compiler knows &str
should target type, , not case because &value1.to_string()
passed clone()
afterwards. when clone()
called on &string
, naturally delegates impl clone string
makes owned copy of string, giving string
. in turn gives type mismatch error:
<anon>:20:34: 4:50 error: mismatched types: expected `&str`, found `collections::string::string` (expected &-ptr, found struct `collections::string::string`) [e0308]
there way work around - can ask &str
out of &string
directly, e.g. explicit reborrowing:
&*value1.to_string()
however, problem arise. whatever temporary value value1.to_string()
returns valid during expression inside used:
map.insert("key1".clone(), (&*value1.to_string()).clone()); // equivalent { let temporary = value1.to_string(); map.insert("key1".clone(), (&*temporary).clone()); } // these braces important!
you can see temporary
destroyed when call insert()
completes, , inserted reference become dangling. rust prevents compiler error.
Comments
Post a Comment