【问题标题】:Getting value from a collection without using the Clone trait在不使用 Clone 特征的情况下从集合中获取价值
【发布时间】:2016-03-08 17:03:37
【问题描述】:

是否可以从集合中获取一个值并对其应用只接受self 而不是&self 的方法?

最小的工作示例

我想写的是类似于:

use std::collections::HashMap;

fn get<B>(key: i32, h: HashMap<i32, Vec<(i32, B)>>) -> i32 where B: Into<i32> {
    let v: &Vec<(i32, B)> = h.get(&key).unwrap();
    let val: &B = v.first().unwrap().1;

    // Do something to be able to call into
    // I only need the value as read-only
    // Does B have to implement the Clone trait?
    return val.into();
}

我曾尝试在编译器错误后到处运球mut试图安抚编译器错误,但这确实是愚蠢的差事。

use std::collections::HashMap;

fn get<B>(key: i32, mut h: HashMap<i32, Vec<(i32, B)>>) -> i32 where B: Into<i32> {
    let mut v: &Vec<(i32, B)> = h.get_mut(&key).unwrap();
    let ref mut val: B = v.first_mut().unwrap().1;
    return (*val).into();
}

这种事情是可能的还是B 必须实现Clone 特征?

我也试过了:

  • 不安全
  • 原始指针

我没试过:

  • Box
  • 我没有遇到的其他 Rust 结构, 我提到这一点是为了明确声明我没有 省略了我知道的任何方法。

【问题讨论】:

    标签: rust readonly move-semantics traits


    【解决方案1】:

    是否可以从集合中获取一个值并应用一个只接受self 而不是&amp;self 的方法?

    一般来说,不,除非从集合中删除它。收藏品拥有价值。采用self 的方法想要在消耗所有权的同时转换项目,因此您必须转移所有权。

    克隆或复制项目会创建具有新所有权的新项目,然后您可以将其赋予该方法。

    在您的特定情况下,您几乎可以摆脱这个令人兴奋的where 子句:

    where for<'a> &'a B: Into<i32>
    

    From&lt;&amp;i32&gt; 除外,i32 没有实现。你可以写一个你想要的特征:

    use std::collections::HashMap;
    
    trait RefInto<T> {
        fn into(&self) -> T;
    }
    
    impl RefInto<i32> for i32 {
        fn into(&self) -> i32 { *self }
    }
    
    fn get<B>(key: i32, h: HashMap<i32, Vec<(i32, B)>>) -> i32
        where B: RefInto<i32>
    {
        let v = h.get(&key).unwrap();
        let val = &v.first().unwrap().1;
    
        val.into()
    }
    
    // ----
    
    fn main() {
        let mut map = HashMap::new();
        map.insert(42, vec![(100, 200)]);
        let v = get(42, map);
        println!("{:?}", v);
    }
    

    或者,您也可以使用Borrow

    use std::collections::HashMap;
    use std::borrow::Borrow;
    
    fn get<B>(key: i32, h: HashMap<i32, Vec<(i32, B)>>) -> i32
        where B: Borrow<i32>
    {
        let v = h.get(&key).unwrap();
        let val = &v.first().unwrap().1;
    
        *val.borrow()
    }
    

    【讨论】:

      【解决方案2】:

      该函数使用HashMap。我假设这是您的意图,因此您不关心它的任何内容,除了您希望转换为 i32 的一个元素。

      您可以使用HashMap::remove 方法提取值。然后您可以使用Vec::swap_remove 提取第一个元素。

      use std::collections::HashMap;
      
      fn get<B>(key: i32, mut h: HashMap<i32, Vec<(i32, B)>>) -> i32 where B: Into<i32> {
          h.remove(&key)
              .unwrap()
              .swap_remove(0)
              .1
              .into()
      }
      

      如果B 复制起来很便宜,那么在复制它的地方编写一个函数会更有意义。

      以上不处理错误。带有错误处理的版本可能如下所示:

      use std::collections::HashMap;
      
      fn get<B>(key: i32, mut h: HashMap<i32, Vec<(i32, B)>>) -> Option<i32> where B: Into<i32> {
          h.remove(&key)
              .and_then(|mut vec| {
                  if vec.is_empty() { None } 
                  else { Some(vec.swap_remove(0).1.into()) }
              })
      }
      

      Vec::swap_remove 并不理想。将任意索引处的元素移出向量而无需任何其他工作的功能将由 IndexMove 特征处理,但该特征尚不存在。

      【讨论】:

      • 我希望 HashMap 仍然将密钥与它拥有的相同向量相关联。
      • @FilipAllberg A.B. 的观点非常很重要。通过接受h: HashMap,您将地图的所有权转让给函数。函数对地图的作用基本上无关紧要,因为其他任何东西都无法使用地图。
      • 注意,现在使用&amp;h。谢谢!
      猜你喜欢
      • 2013-03-25
      • 2010-11-24
      • 1970-01-01
      • 2020-03-05
      • 2020-02-24
      • 1970-01-01
      • 2018-06-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多