Reputation: 471
A struct is defined as:
struct Node {
set: HashSet<usize>,
// other fields omitted
}
I have to implement a function for a trait (compatibility issues) which needs to return all elements in the set as a slice.
I am aware of something like the following function won't work:
impl Node {
pub fn set_slice(&self) -> &[usize] {
let elems: Vec<_> = self.set.iter().cloned().collect();
&elems[..]
}
}
The problem is:
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 | | }
| |_____^
I know this requirement may sound strange. Despite why I have to do this, is there any 'good' way to achieve this?
If it is possible, I want keep the HashSet
container for a O(1) lookup, and I don't want to introduce new struct members in order to save memory.
Upvotes: 4
Views: 6262
Reputation: 430554
No, your requirements are 100% completely impossible in safe Rust.
A HashSet
/ HashMap
do not have a contiguous collection of data, thus there's no way to get a slice from them.
If you can change things, then you have options.
You can "render a view" of the HashSet
if you can store a Vec
and the method is &mut self
:
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
}
}
You could return a Cow
which would be either borrowed or owned:
use std::borrow::Cow;
impl Node {
pub fn set_slice(&self) -> Cow<[usize]> {
self.set.iter().cloned().collect::<Vec<_>>().into()
}
}
You could return an iterator over the values:
impl Node {
pub fn set_slice<'a>(&'a self) -> impl Iterator<Item = &'a usize> + 'a {
self.set.iter()
}
}
There's possibly a crate that uses a tightly-packed Vec
as its backing storage, which could then be exposed as a slice.
Upvotes: 7
Reputation: 497
That is impossible in simple (basic) ways.
That's possible with Box
, mut static
but I recommend to modify your trait and return something like in following example:
You can use AsRef<[T]>
instead of &[usize]
in your trait. Or simply return an iterator.
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);
}
This is only two ways from many.
Upvotes: 0