Getting value from a collection without using the Clone trait

为君一笑 提交于 2019-12-10 16:13:13

问题


Is it possible to get a value from a collection and apply a method to it which accepts only self and not &self?

Minimal Working Example

What I would like to write is something akin to:

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();
}

I have tried in vain to dribble mut here and there to try to appease compiler error after compiler error, but this is really a fool's errand.

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();
}

Is this sort of thing even possible or does B have to implement the Clone trait?

I've also tried:

  • Unsafe
  • Raw pointers

I've not tried:

  • Box
  • Other Rust constructs that I have not encountered, I mention this to explicitly state that I have not omitted any approaches that I know of.

回答1:


Is it possible to get a value from a collection and apply a method to it which accepts only self and not &self?

In general, no, not without removing it from the collection. The collection owns the value. Methods that take self want to transform the item while consuming the ownership, so you have to transfer ownership.

Cloning or copying an item creates a new item with new ownership that you can then give to the method.

In your particular case, you can almost get away with this exciting where clause:

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

Except From<&i32> is not implemented for i32. You can write a trait that does what you want though:

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);
}

Alternatively, you might be able to make use of 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:


The function consumes the HashMap. I'm assuming this is your intent and that you therefore don't care about any of its content except the one element you wish to convert into an i32.

You can use the HashMap::remove method to extract a value. You can then use Vec::swap_remove to extract the first element.

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()
}

If B is cheap to copy, then it makes more sense to write a function where it is copied.

The above doesn't handle errors. A version with error handling could look like this:

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 isn't ideal. The functionality of moving an element at an arbitrary index out of the vector without any other work would be handled by the IndexMove trait which doesn't yet exist though.



来源:https://stackoverflow.com/questions/35873497/getting-value-from-a-collection-without-using-the-clone-trait

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!