简体   繁体   中英

How can I implement an iterator over an enum which contains slice values of different types converting all values to String?

This is a simplified example of my enum :

#[derive(Debug, Clone, Copy)]
enum Data<'a> {
    I32(&'a [i32]),
    F64(&'a [f64]),
}

I use the enum to store different slice types (not only &[i32] and &[f64] as in the example above, but many more) to the same vector Vec<Data<'a>> . I need a way to iterate through the slice values (either &[i32] or &[f64] , whatever is stored in the enum ) converting all values to String . The following code shows what in principle I would like to achieve, but it does not work:

impl<'a> Data<'a> {
    fn iter_to_string(&self) -> impl Iterator<Item = String> {
        match self {
            Data::I32(data) => data.iter().map(|&x| x.to_string()),
            Data::F64(data) => data.iter().map(|&x| x.to_string()),
        }
    }
}
error[E0308]: match arms have incompatible types
  --> src/main.rs:9:9
   |
9  | /         match self {
10 | |             Data::I32(data) => data.iter().map(|&x| x.to_string()),
11 | |             Data::F64(data) => data.iter().map(|&x| x.to_string()),
   | |                                ----------------------------------- match arm with an incompatible type
12 | |         }
   | |_________^ expected i32, found f64
   |
   = note: expected type `std::iter::Map<std::slice::Iter<'_, i32>, [closure@src/main.rs:10:48: 10:66]>`
              found type `std::iter::Map<std::slice::Iter<'_, f64>, [closure@src/main.rs:11:48: 11:66]>`

You have two options:

  1. Dynamic dispatch
  2. A non-lazy iterator

You can't have both static dispatch and a lazy iterator, because that requires the compiler to produce the code path for to_string() at compile time where the correct choice is only known at runtime.

Dynamic dispatch

To use dynamic dispatch, you can create an iterator over trait objects.

use std::fmt::Display;

struct DisplayIter<'iter> {
  inner_iter: Box<Iterator<Item=&'iter dyn Display> + 'iter>,
}

impl<'iter> Iterator for DisplayIter<'iter> {
  type Item = String;

  fn next(&mut self) -> Option<String> {
    self.inner_iter.next().map(|val| format!("{}", val))
  }
}

In order to use it on a slice iterator, though, you'll need to transform each reference into a trait object reference, such as

data.iter().map(|x| x as &dyn Display)

Non-lazy iterator

Just collect the results of the map you're already doing, and return a owned iterator over the result. This has the downside of allocating, but it solves the issue of needing to choose the code path at compile-time.

data.iter().map(|&x| x.to_string()).collect::<Vec<_>>().into_iter()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM