Need help with generic Iterator adapter

I'm writing some bindings with cxx crate.
My idea is to write a generic Iterator adapter over cxx::UniquePtr<cxx::CxxVector<T>> where T is an opaque FFI type, so that this iterator would yield Rust wrappers around foreign type T

It should be generic as I have multiple FFI types. Here's I'm trying to wrap a single one:

pub struct Product<'a> {
    inner: &'a ffi::Product
}
pub struct Elements<T> where
    T: cxx::VectorElement
{
    data: cxx::UniquePtr<cxx::CxxVector<T>>,
}

My goal is to have an API like this:

let products: Elements<ffi::Product> = ffi::get_products();
for prod in products {
     /// prod is &Product, not &ffi::Product
}

Here's where I stuck, I tried impl Iterator but it needs impl in type alias which is unstable:


impl<T: VectorElement> Iterator for Elements<T> {
    type Item = impl Wrap<'a> // unstable
}

pub trait Wrap<'a> {
    type NewType;

    fn wrap(&'a self) -> Self::NewType;
}

impl<'a> Wrap<'a> for ffi::Product {
    type NewType = Product<'a>;

    fn wrap(&'a self) -> Product<'a> {
        Product{inner: self}
    }

}

Tried this too, but not sure how to make Item = Wrap::NewType:

impl<VectorElement> Elements<T>

  pub fn iter<'a>(&'a self) -> impl Iterator<Item = ???> {
     ....
    }

This playground is basically how you'd implement an iterator adapter for Rust's Vec. Commented is how I think it could be done for cxx::Vector, I don't have any local project to test it with though, and since I can't compile it, it should be full of mistakes. The spirit of the thing is basically the same.

1 Like

Hey @erelde, close, but how do I make it return a Wrapper<&T> type and not &T?

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=342b5c6e3f9173f845b13fdaddc7f214

Here's an example that returns Wrapped<'a, T>:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=40c31c7bfed654515eec17c3e23d0a31

Thanks @mbrubeck ! Yes this works for a concrete type. Sorry I wasn't specific enough, the solution I'm looking for is when Wrapped is not so much generic but rather different concrete types, so

/// Db API:
fn get_products(&self) -> ProductsIterator {    // should yield &Product
    let prods: Elements<ffi::Product> = ffi::get_products();
    ProductsIterator { prods }
}

fn get_folders(&self) -> FoldersIterator {
    let folders: Elements<ffi::Folders> = ffi::get_folders();
    FoldersIterator { folders }
}

With the approach you and @erelde posted I would have to create a concrete *Iterator struct for each FFI type and impl Iterator and IntoIterator for each of them and I was hoping to avoid that.

My ideal solution would be

fn get_xxx(&self) -> impl Iterator<Item = Xxx<'a>>

I'll stick to concrete types, it works for me. and I might just write a macro_rules! for automation.

Thank you guys, appreciate your help!

pub struct Products {
    vec: cxx::UniquePtr<cxx::CxxVector<ffi::Product>>,
}

pub struct ProductsIter<'a> {
    iter: cxx::vector::Iter<'a, ffi::Product>
}

impl<'a> IntoIterator for &'a Products {
   type Item = crate::Product<'a>;
    type IntoIter = ProductsIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        ProductsIter {iter: self.vec.iter() }
    }
}

impl<'a> Iterator for ProductsIter<'a> {
    type Item = crate::Product<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        Some(crate::Product{
            ptr: CxxRef::Borrow(self.iter.next()?)
        })
    }
}

1 Like

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.