1

I want to create safe interface in Rust for a C library. That library provides a generator function which can be called multiple times on some source (eg. file) pointer yielding pointers to subsequent elements of that source - basically it's an iterator.

Some useful things can be done with that element pointer, but after the generator function is called again the previous pointer becomes invalid. The element can also be safely cloned (which is what I'm using now) but that comes with significant performance penalty, which in many use cases is completely unnecessary.

I want to create a safe wrapper interface similar to the C interface. My current idea is that I wrap element pointer in struct Element and source pointer with struct Source that also holds currently valid Element.

struct Source {
    current_element: Element,
    source_pointer: *mut S,
}

// things can be done with &Element directly
// or it can be safely cloned to Element
struct Element {
    element_pointer: *mut E,
}

To wrap that generator behavior, I want to implement Iterator for Source that in each iteration generates new element_pointer, wraps it, assigns to Source and returns reference to current_element that is valid only until .next() is again called. Something like this:

impl Iterator for Source {
    type Item = &Wrapper;

    fn next(&mut self) -> Option<Self::Item> {
        let new_pointer = unsafe{generator_ffi_function(self.source_pointer)};

        if new_pointer.is_null() {
            None
        } else {
            self.current_wrapper.element_pointer = new_pointer;
            Some(&self.current_wrapper)
        }
    }
}

But I cannot solve how to use lifetimes in this scenario. Can the intended interface be implemented in Rust, and if so how?

This solution does not work, because when string_holder being &'a mut breaks things. This example wraps similar C interface, but returns pointers so isn't safe. Many questions also refer to implementing mutable iterator but I don't want to give &mut, and those solutions also assume that only one reference exists, which I cannot assure.

1
  • 2
    Using a recent Rust, you could use GATs, that is pretty much their use case. There is no iterator-style syntactic sugar around iterators though. This post on non-GAT iterators is also an option. Either way, Iterator is not feasible.
    – Masklinn
    Commented Feb 1, 2024 at 14:24

1 Answer 1

1

This is exactly the so-called "lending iterator" or "streaming iterator", discussed many times. There is no syntactic language support for them like for loop as of today, but the LendingIterator trait can be written now that Generic Associated Types are stabilized (and even before):

pub trait LendingIterator {
    type Item<'a>
    where
        Self: 'a;

    fn next(&mut self) -> Option<Self::Item<'_>>;

    // Adapter methods...
}

Such trait is provided by the lending-iterator crate (though without GATs), but if all you want is a single iterator type, it is simpler to just define an inherent next() method. It can be iterated with a while let loop:

let mut iter = ...;
while let Some(v) = iter.next() {
    // ...
}

You do lose the adapters, though.

Not the answer you're looking for? Browse other questions tagged or ask your own question.