I want a function to be able to pass a vector (or slice), or a sequence made on the fly:
case 1: my_func(&item_ids_vec)
case 2: my_func([extra_item_id].into_iter().chain(item_ids_vec.iter())
What are the options?
items: &[ItemId]
won't work with case 2.
items: impl Iterator<Item=ItemId>
won't work, because it allows only one particular impl
.
dyn Iterator<Item=ItemId>
might be slow, and causes some weird compliations errors, I can't find a way to fix.
dyn Iterator<Item=&ItemId>
-- compiler is unhappy. Says lifetimes are unstable.
in case 2, compose a new vector before calling (or reuse one to reduce allocations). Argh, ugly!
Any ideas how to write this kind of function?
A sketch on Rust playground :
#[derive(Debug)]
struct ItemId(usize);
enum Package {
Combo { first: ItemId, other: Vec<ItemId> },
Simple { items: Vec<ItemId> }
}
fn process_sequence(items: &dyn Iterator<Item=ItemId>) {
for i in items {
println!("item {i:?}");
}
}
fn main() {
let packs = vec![
Package::Simple { items: vec![ItemId(1), ItemId(2), ItemId(3)] },
Package::Combo { first: ItemId(10), other: vec![ItemId(12), ItemId(13), ItemId(14)] }
];
for p in packs {
match p {
Package::Simple { items } => process_sequence(&items.into_iter()),
Package::Combo { first, other } => {
process_sequence(&[first].into_iter().chain(other))
}
}
}
}
You can use generics.
Notice that depending on what you wnat to do with T you might need to bind it to other traits, like I did with Debug.
use core::fmt::Debug;
fn process_sequence<I, T>(items: I) where I: Iterator<Item = T>, T: Debug {
for i in items {
println!("item {i:?}");
}
}
2 Likes
Oh, now I got it. Actually, my initial version with impl
was the same generic as you suggest, and I mistakenly thought it must be the same thing. But in fact, one can call it with different types from the outside, and it will compile to 2 different functions.
So I just edited my one better:
fn process_sequence(items: impl Iterator<Item=ItemId>) {
for i in items {
println!("item {i:?}");
}
}
...
Package::Simple { items } => process_sequence(items.into_iter()),
Package::Combo { first, other } => {
process_sequence(&mut [first].into_iter().chain(other))
}
I only can't understand why compiler wants &mut [first]
there.
1 Like
Huum, I'm not sure why its asking that, can you post the compiler error message ?
Actually, there are two problems, the first is that its thinking you're trying to pass an immutable reference to a Chain iterator.
Doing this it will be clear you want to create an iterator out of a slice of ItemId
:
process_sequence((&[first]).into_iter().chain(other))
However since you're using a slice, that will be an iterator of &ItemId
which would cause another problem because your process_sequence function is not expecting an iterator of &ItemId
.
Just use an array istead of a slice:
process_sequence([first].into_iter().chain(other))
If you really want to pass ItemId
arround as a reference you'll need to change a few things.
1 Like
A follow-up: after returning to the problem, I noticed
I couldn't consume the original structure, hence references and endless compile errors.
The first element of the sequence had to be distinguished always.
So I just decided to separate the first element in another argument:
fn process_sequence(first: &ItemId, rest: &[ItemId]) {
...
}
process_sequence(&simple[0], &simple[1..]);
process_sequence(&first, &other);
The signature isn't pretty, but the function is only used to de-duplicate some heavy lifting code, just these 2 places, so this will suffice.
system
Closed
July 7, 2024, 8:04pm
6
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.