我正在尝试理解宏和&利用一个简单的宏来创建类似于vec!
的散列图。出于某种原因,如果我将String
转换为宏内的&str
,它似乎无效。
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();
}
答案 0 :(得分:4)
这是works()
函数的有效正文:
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);
}
这是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);
}
在clone()
上调用&str
时,它只是胖指针(两个指针大小的数字)的简单字节副本:共享引用总是Copy
;克隆的引用也与原始引用具有相同的生命周期。这在works()
中是可以的,因为在那里使用的三个字符串切片指向静态内存,因为它们是字符串文字,第四个指向一个临时局部变量,它一直存在于函数的末尾:
let value1str: &str = &value1.to_string();
// is in fact equivalent to
let temporary = value1.to_string();
let value1str: &str = &temporary;
因为这个temporary
比哈希映射更长,所以对它的引用可以存储在这个哈希映射中:地图首先被破坏,永远不会有机会持有悬空引用。
但是,在第二种情况下,对clone()
类型的值调用&String
,因为&s
s: String
只有&str
才会给您&str
编译器知道&value1.to_string()
应该是目标类型,而事实并非如此,因为clone()
之后会传递给clone()
。在&String
上调用impl Clone for String
时,它会自然委托给String
,它只会创建另一个字符串副本,并为您提供<anon>:20:34: 4:50 error: mismatched types:
expected `&str`,
found `collections::string::String`
(expected &-ptr,
found struct `collections::string::String`) [E0308]
。这反过来又会出现类型不匹配错误:
&str
有一种解决方法 - 您可以直接从&String
提出&*value1.to_string()
,例如明确重新借贷:
value1.to_string()
然而,现在会出现另一个问题。无论临时值map.insert("key1".clone(), (&*value1.to_string()).clone());
// is roughly equivalent to
{
let temporary = value1.to_string();
map.insert("key1".clone(), (&*temporary).clone());
} // these braces are important!
返回什么,仅在使用它的表达式中有效:
temporary
您可以看到insert()
的调用完成时会立即销毁mips_cpu.h
,并且插入的引用将变为悬空。 Rust会因编译错误而阻止这种情况。