Reputation: 83
I am trying to create an Iterator for a trait that allows trait objects. My extremely simplified and contrived example would be something like this:
trait MyTrait<T>
where
T: 'static,
{
fn store(&mut self, t: T);
fn access(&self, i: usize) -> T;
fn iter(&self) -> MyIter<T> {
MyIter::new(self)
}
}
struct MyIter<'a, T: 'static> {
collection: &'a dyn MyTrait<T>,
j: usize,
}
impl<'a, T: 'static> MyIter<'a, T> {
fn new(collection: &dyn MyTrait<T>) -> Self {
Self {
collection: collection,
j: 0,
}
}
}
impl<'a, T: 'static> Iterator for MyIter<'a, T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.j += 1;
Some(self.collection.access(self.j))
}
}
but this gives
error[E0277]: the size for values of type `Self` cannot be known at compilation time
--> src/main.rs:10:21
|
10 | MyIter::new(self)
| ^^^^ doesn't have a size known at compile-time
|
= note: required for the cast from `Self` to the object type `dyn MyTrait<T>`
help: consider further restricting `Self`
|
9 | fn iter(&self) -> MyIter<T> where Self: Sized {
Now, I understand that the compiler wants Self
to be Sized
and I could just add the where clause. However, I would like to implement MyTrait
for other traits that are not Sized
. In fact, as they need to be object traits, I cannot add Sized
. How could I accomplish that.
Whatever I try, I run into a Sized
problem. Is there a general solution or a tutorial for implementing Iterators in rust? Unfortunately, the only examples I have found are either iterators for structs or use unsafe code. As the Sized
requirement is for the casting to dyn ... even boxing dyn ... won't be a solution as far as I can tell.
Upvotes: 2
Views: 200
Reputation: 71075
The problem is that if Self
is, for example, a trait object, then &Self
is a wide pointer (two words), and we cannot convert it to trait object because we need more indirection for that (we don't have where to store the metadata).
A possible solution is to use generics instead of trait objects, but if you do want trait objects, you can use Box
to create another indirection:
trait MyTrait<T>
where
T: 'static,
{
fn store(&mut self, t: T);
fn access(&self, i: usize) -> T;
fn iter(&self) -> MyIter<T> {
MyIter::new(Box::new(IterRefAdapter(self)))
}
}
struct IterRefAdapter<'a, U: ?Sized>(&'a U);
impl<U: ?Sized + MyTrait<T>, T: 'static> MyTrait<T> for IterRefAdapter<'_, U> {
fn store(&mut self, t: T) {
panic!("cannot call `store()` via shared reference");
}
fn access(&self, i: usize) -> T {
U::access(self.0, i)
}
}
struct MyIter<'a, T: 'static> {
collection: Box<dyn MyTrait<T> + 'a>,
j: usize,
}
impl<'a, T: 'static> MyIter<'a, T> {
fn new(collection: Box<dyn MyTrait<T> + 'a>) -> Self {
Self { collection, j: 0 }
}
}
impl<'a, T: 'static> Iterator for MyIter<'a, T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.j += 1;
Some(self.collection.access(self.j))
}
}
Upvotes: 2