类型推断的奇怪宏行为
Weird macro behavior for type inference
我正在尝试理解宏并利用一个简单的宏来创建类似于 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();
}
这是您的 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);
}
当在 &str
上调用 clone()
时,它只是一个简单的按字节复制的胖指针(两个指针大小的数字):共享引用总是 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()
。当在 &String
上调用 clone()
时,它自然会委托给 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]
有一种方法可以解决它 - 您可以直接从 &String
中询问 &str
,例如明确再借:
&*value1.to_string()
但是,现在又会出现一个问题。任何临时值 value1.to_string()
returns 仅在使用它的表达式中有效:
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!
你可以看到当insert()
调用完成时temporary
会立即被销毁,插入的引用会变成悬空。 Rust 通过编译器错误来防止这种情况。
我正在尝试理解宏并利用一个简单的宏来创建类似于 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();
}
这是您的 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);
}
当在 &str
上调用 clone()
时,它只是一个简单的按字节复制的胖指针(两个指针大小的数字):共享引用总是 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()
。当在 &String
上调用 clone()
时,它自然会委托给 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]
有一种方法可以解决它 - 您可以直接从 &String
中询问 &str
,例如明确再借:
&*value1.to_string()
但是,现在又会出现一个问题。任何临时值 value1.to_string()
returns 仅在使用它的表达式中有效:
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!
你可以看到当insert()
调用完成时temporary
会立即被销毁,插入的引用会变成悬空。 Rust 通过编译器错误来防止这种情况。