For a project I'm working on (an implementation of an obscure programming language), I have an enum Data
, which represents data the interpreter is keeping track of (e.g., a variable). This language has infinite lists/lazy arrays, so I'm using a dyn Iterator
for those. Since these lists can be stored in Data
, and they themselves contain Data
, it's a recursive data structure.
This works fine, until I need to clone some Data
, which happens quite often. Currently, Data
looks something like this:
pub enum Data {
Float(f64),
Int(BigInt),
String(String),
Array(Box<dyn Iterator<Item = Data>>)
}
And if I try to #[derive(Clone)]
for it:
error[E0277]: the trait bound `dyn Iterator<Item = Data>: Clone` is not satisfied
--> src/shorispo.rs:28:11
|
23 | #[derive(Clone)]
| ----- in this derive macro expansion
...
28 | Array(Box<dyn Iterator<Item = Data>>)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `dyn Iterator<Item = Data>`
|
= note: required because of the requirements on the impl of `Clone` for `Box<dyn Iterator<Item = Data>>`
= note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0277`.
I've tried dyn-clone
, but this seems to create a sort of chicken-and-egg problem, since Data
would need to be Clone
.
Is there a way I can do this?
You have to implement Clone manually for your type. And to Clone a trait object requires some special sauce, which I derived from this answer.
// We have to provide a special trait for our clonable iterator,
// since Clone requires a Sized type (so we can't call it on a trait object).
trait CloneIterator: Iterator {
fn clone_box(&self) -> Box<dyn CloneIterator<Item = Self::Item>>;
}
// Implement our special trait for all Cloneable Iterators
impl<T> CloneIterator for T
where
T: 'static + Iterator + Clone,
{
fn clone_box(&self) -> Box<dyn CloneIterator<Item = Self::Item>> {
Box::new(self.clone())
}
}
// Use our special trait in Data instead
pub enum Data {
Float(f64),
Int(i64),
String(String),
Array(Box<dyn CloneIterator<Item = Data>>),
}
// Implement Clone manually, making use of `clone_box` for our special trait
impl Clone for Data {
fn clone(&self) -> Self {
match self {
Self::Float(f) => Self::Float(f.clone()),
Self::Int(i) => Self::Int(i.clone()),
Self::String(s) => Self::String(s.clone()),
Self::Array(b) => Self::Array(b.clone_box()),
}
}
}