< 返回版块

leowei1234567 发表于 2022-12-05 15:25

Tags:string类型的as_str()和&string有什么区别吗

定义一个HashMap,如下,在get的时候,如果传入value.as_str()编译通过,传入&value提示 the trait Borrow<std::string::String> is not implemented for &str, value.as_str()的类型不也是&str吗

lazy_static! {
    static ref OPERATIONS_MAP: HashMap<&'static str, u32> = {
        let mut map = HashMap::new();
        map.insert("create user", 1);
        map.insert("delete user", 2);
        map
    };
}

impl LogOperation {
    fn parse(value: String) -> Result<LogOperation, String> {
        if let Some(operation_number) = OPERATIONS_MAP.get(&value) {
        //if let Some(operation_number) = OPERATIONS_MAP.get(value.as_str())
            return Ok(Self(*operation_number));
        }
        return Err(format!("{} is not a valid operation.", value));
    }
}

评论区

写评论
作者 leowei1234567 2022-12-06 10:34

感谢各位回答, 又学习到了

苦瓜小仔 2022-12-05 16:16

你需要的是 OPERATIONS_MAP.get(&*value),或者任何得到直接 &str 的方法。

尽量去理解编译器告诉你的信息:

error[E0277]: the trait bound `&str: Borrow<String>` is not satisfied
  --> src/lib.rs:14:60
   |
14 |         if let Some(operation_number) = OPERATIONS_MAP.get(&value) {
   |                                                        --- ^^^^^^ the trait `Borrow<String>` is not implemented for `&str`
   |                                                        |
   |                                                        required by a bound introduced by this call
   |
   = help: the trait `Borrow<str>` is implemented for `String`
note: required by a bound in `HashMap::<K, V, S>::get`
// impl<K, V> HashMap<K, V, RandomState>
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where
    K: Borrow<Q>,
    Q: Hash + Eq,

已经确定 K = &strV = u32Q = String,那么根据约束条件,你能得到 &str: Borrow<String> 吗?

编译器正是告诉你不能得到 —— 去搜索 Borrow trait 文档,发现这么几条相关的实现:

impl Borrow<str> for String // String: Borrow<str>
impl<T> Borrow<T> for &T // &T: Borrow<T>
impl<T> Borrow<T> for &mut T // &mut T: Borrow<T>
impl<T> Borrow<T> for T // T: Borrow<T>

仔细比对(固定 K = &str),只有:

&str: Borrow<str> // 其实只有这一条
&mut str: Borrow<str>
str: Borrow<str>

显然没有 &str: Borrow<String>

&str: Borrow<String> 其实是不可能的,根据定义:

pub trait Borrow<Borrowed>
where
    Borrowed: ?Sized,
{
    const fn borrow(&self) -> &Borrowed;
}
// 针对  `&str: Borrow<String>` 解糖:<&str as Borrow<String>>::borrow(&&str) -> &String
// 嗯,显然这不可能得到

当传入 &*value 之类的值,其类型为 &str,即 Q = str,有 &str: Borrow<str>,所以代码通过。

总结:Rust 的泛型和方法是靠推导的。


嗯,继续思考的话,可能会问,&String&str 不是一样吗? coercion 就让代码通过了啊。

只要你在某个地方明确指明类型,当然可以利用 coercion 进行转换: OPERATIONS_MAP.get::<str>(&value)

但没指明任何类型的泛型情况下,Rust 不对 &String 做强制转换,也没有错。

A coercion can only occur at certain coercion sites in a program; these are typically places where the desired type is explicit or can be derived by propagation from explicit types (without type inference). src: https://doc.rust-lang.org/reference/type-coercions.html

Pikachu 2022-12-05 16:11

我感觉是因为HashMap的get参数类型是generic的,所以coercion没能正常工作。

展开解释一下:

正常情况下如果形参类型是&str而实参类型是&String,因为String实现了Deref<Target=str>,所以可以coerce过去。

但是对于get这个函数,它的参数类型是Q且要求满足K: Borrow,也就是要求HashMap的key: K能产生一个Q的借用(背后的设计思路是HashMap是持有所有key的ownership的,所以总可以对K随便借用)。看起来rustc在处理这种形参类型不确定的情况时,不会做coercion。

  • https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.get

  • https://doc.rust-lang.org/reference/type-coercions.html#coercion-sites

  • https://doc.rust-lang.org/std/borrow/trait.Borrow.html

1 共 3 条评论, 1 页