如何从HashSet中创建一个slice?

3

结构体的定义如下:

struct Node {
    set: HashSet<usize>,
    // other fields omitted
}

我需要实现一个trait的函数(由于兼容性问题),该函数需要将集合中的所有元素作为切片返回。

我知道以下类似的函数不起作用:

impl Node {
    pub fn set_slice(&self) -> &[usize] {
        let elems: Vec<_> = self.set.iter().cloned().collect();
        &elems[..]
    }
}

问题是:

error[E0597]: `elems` does not live long enough
  --> src/main.rs:11:10
   |
11 |         &elems[..]
   |          ^^^^^ borrowed value does not live long enough
12 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the method body at 9:5...
  --> src/main.rs:9:5
   |
9  | /     pub fn set_slice(&self) -> &[usize] {
10 | |         let elems: Vec<_> = self.set.iter().cloned().collect();
11 | |         &elems[..]
12 | |     }
   | |_____^

我知道这个要求可能听起来很奇怪。尽管我为什么要这样做,但有没有什么“好”的方法可以实现这一点?

如果可能的话,我想保留 HashSet 容器以进行O(1)查找,并且我不想引入新的结构成员以节省内存。


1
这里可能存在一个XY问题。考虑在使用该特性时提供更多的上下文。同时要记住,拥有容器(如Vec)可以强制转换为切片。 - undefined
2个回答

7

不,你的要求在安全的Rust中是完全不可能的。

HashSet / HashMap没有连续的数据集合,因此无法从它们获取一个切片。


如果您能改变事物,那么您就有选择的余地。
如果您可以存储一个Vec,并且方法是&mut self,那么您可以“渲染HashSet的视图”。
struct Node {
    set: HashSet<usize>,
    view: Vec<usize>,
    // other fields omitted
}

impl Node {
    pub fn set_slice(&mut self) -> &[usize] {
        self.view.clear();
        self.view.extend(self.set.iter().cloned());
        &self.view
    }
}

你可以返回一个 Cow,它可以是借用的或拥有的:
use std::borrow::Cow;

impl Node {
    pub fn set_slice(&self) -> Cow<[usize]> {
        self.set.iter().cloned().collect::<Vec<_>>().into()
    }
}

你可以返回值的迭代器
impl Node {
    pub fn set_slice<'a>(&'a self) -> impl Iterator<Item = &'a usize> + 'a {
        self.set.iter()
    }
}

可能有一个使用紧密打包的Vec作为其后备存储的板条箱,可以将其公开为一个片段。


谢谢你的回答。给临时变量elems添加生命周期约束会起作用吗? - undefined
2
@Yang 不是开玩笑,我说的是不可能。如果你不能改变HashSet或者trait,并且也不会添加字段,那就没有解决办法。 - undefined
我正在尝试使用Cow,一旦我有了let cow = Cow::from(&slice[..])或者let cow = Cow::from(vec),我如何获取回&slice[..]呢?我已经尝试使用了cow.borrow(),但是出现了错误type annotations required: cannot resolve 'std::borrow::Cow<'_, [u32]>: std::borrow::Borrow<_>' - undefined
@Yang &cow[..] ? - undefined

0

用简单(基本)的方式是不可能的。

使用Boxmut static是可能的,但我建议修改你的trait并返回类似下面示例中的内容:

你可以在你的trait中使用AsRef<[T]>代替&[usize]。或者直接返回一个迭代器。

struct Node {
    set: HashSet<usize>,
}

trait SetSlice {
    type Slice: AsRef<[usize]>;
    fn get_slice_cloned(&self) -> Self::Slice;
}

impl SetSlice for Node {
    type Slice = Vec<usize>;
    fn get_slice_cloned(&self) -> Self::Slice { self.set.iter().cloned().collect() }
}

// there we use auto-impl of Iterator trait
// and return the iter.
// NOTE: we cannot use auto-impl in trait methods.
impl Node {
    fn get_neat_iter(&self) -> impl Iterator<Item = &usize> { self.set.iter() }
}

fn need_slice(slice: &[usize]) {}

fn main() {
    let n = Node { set: Default::default(), };

    // as_ref
    let all = n.get_slice_cloned();
    need_slice(all.as_ref());

    // iter-way
    let all: Vec<_> = n.get_neat_iter().cloned().collect();
    need_slice(&all);
}

这只是众多方法中的两种。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接