How to share implementation between Iterator and IntoIterator?

I've got a collection that is pretty tricky to iterate over, and I'd prefer not to implement that iteration twice. What I'd love to be able to do would be to take my Iter<'a> which holds a reference to my SetU64, and create an IntoIter that looks something like:

struct IntoIter {
  set: SetU64,
  iter: Iter<'???>,
}

where the iterator holds a reference to the set. This could in principle be move-safe because the reference is actually to data on the heap, but it would not be safe if the set were dropped or modified.

I'm doubtful that the above could work, but I wonder if there is another solution to sharing code between an iterator and "into iterator" that folks have come up with?

It's not possible. Structs in Rust can't reference anything inside themselves, ever. Search for "self-referential struct" for more info about this.

I was hoping there was another idiom that would work for sharing implementation. Since in principle it's a very sane thing to do, it would be lovely if there were something prettier than creating a macro for the multiple identical implementations.

You could try implementing an iterator for Cow<Set> or perhaps AsRef<Set>. Or just reuse a standalone next() function in both implementations.

1 Like

Actually, now that I think about it, I could do something akin to this safely by leaking my set. Which raises the possibility of leaking the set, then storing a raw pointer that I (with unsafe code) free on drop...

That unfortunately doesn't help much, because the challenge is that there are multiple variants, some of which can .iter().max() efficiently using different implementations. So my Iter has custom versions of max, min, last, count, etc, which is why I don't care to duplicate it. But I think a little work with raw pointers will do the trick. My set already requires unsafe raw pointer use, so it's not a big deal, and the code reuse addresses bugs that have already cropped up.

This is greatly overstated.

Obviously, using unsafe, you can create a self-referential struct, and even expose a safe interface. The Pin type is explicitly designed to help with this. There are also crates such as rental that encapsulate the unsafety so you don't risk anything. But you could also just replace '??? with 'static, use one little bit of unsafe to relax the lifetime, and as long as you don't expose methods on IntoIter that could invalidate the Iter, it would be fine.

But even without unsafe, it's still perfectly possible to create self-referential structs. This works fine:

#[derive(Debug)]
struct Foo<'a> {
    this: Option<&'a i32>,
    data: i32,
}

let mut foo = Foo { this: None, data: 10 };
foo.this = Some(&foo.data);
println!("{:?}", foo);

It's not movable and it can't have drop glue, but it's definitely self-referential. You can actually do a lot with stuff like this, but you have to design the API around only shared references, which is why a lot of people just avoid self-referential structs entirely. But it's not as simple as "self-referential structs are not possible".

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.