Reputation: 1011
I have a reasonably simple function (let's call it intersection) that takes two parameters of type &[u32]
and I'd like the return type to be &[u32]
. This function takes in two slices(arrays?), and returns a new slice(array?) containing elements that are in both slices.
pub fn intersection<'a>(left: &'a [u32], right: &'a [u32]) -> &'a [u32] {
let left_set: HashSet<u32> = left.iter().cloned().collect();
let right_set: HashSet<u32> = right.iter().cloned().collect();
// I can't figure out how to get a
// `&[u32]` output idiomatically
let result: &[u32] = left_set
.intersection(&right_set)
.into_iter()
.....
.....
result //<- this is a slice
}
I suppose I could do something like create a Vec<u32>
but then borrow checker doesn't like me returning that Vec<u32>
.
pub fn intersection<'a>(left: &'a [u32], right: &'a [u32]) -> &'a [u32] {
.....
.....
let mut result: Vec<u32> = left_set
.intersection(&right_set)
.into_iter()
.cloned()
.collect();
result.sort();
result.as_slice() //<-- ERROR cannot return reference to local variable
// `result` returns a reference to data owned by the current function
}
I'm probably missing a trick here. Any advice on how to do this idiomatically in Rust?
Upvotes: 3
Views: 2726
Reputation: 42492
This function takes in two arrays
No, it takes two slices.
I'm probably missing a trick here. Any advice on how to do this idiomatically in Rust?
There is no trick and you can't. A slice is a form of borrow, by definition a slice refers to memory owned by some other collection (static memory, a vector, an array, ...).
This means like every other borrow it can't be returned if it borrows data from the local scope, that would result in a dangling pointer (as the actual owner will get destroyed when the scope ends).
The correct thing to do is to just return a Vec
:
pub fn intersection<'a>(left: &'a [u32], right: &'a [u32]) -> Vec<u32> {
left.iter().collect::<HashSet<_>>().intersection(
&right.iter().collect()
).map(|&&v| v).collect()
}
Or if it's very common for one of the slices to be a subset of the other and you're happy paying for the check (possibly because you can use something like a bitmap) you could return a Cow
and in the subset case return the subset slice:
pub fn intersection<'a>(left: &'a [u32], right: &'a [u32]) -> Cow<'a, [u32]> {
if issubset(left, right) {
Cow::Borrowed(left)
} else if issubset(right, left) {
Cow::Borrowed(right)
} else {
Cow::Owned(
left.iter().collect::<HashSet<_>>().intersection(
&right.iter().collect()
).map(|&&v| v).collect()
)
}
}
Upvotes: 5