Constraining associated value of trait objects (specifically Iterator)

Hi,

I'm new to Rust and am writing one of my first programs, but I'm familiar with c++.

Please can you let me know if there a way for me to deal with the error 'dyn Iterator<Item = &MyType>' doesn't implement 'Debug'?

MyType implements Debug.

I have tried searching for this and tried various ways of addressing it but none worked. At one point I derived a new trait DebugIterator, with supertraits of Iterator and Debug, and that led me stumbling to here (Tracking issue for RFC 2289, "Associated type bounds" · Issue #52662 · rust-lang/rust · GitHub) but I am not sure if it means my approach isn't going to work.

I'm not sure if further context would be useful or appreciated, but here is the background just in case the idiomatic solution is for an overhaul rather than fixing this specific error!

I am trying to write a series of trait implementations that will convert a number of types into fewer more generic types in a Enum (as part of a process towards serializing them and just to learn). I am aware of the serde crate, but I would like to write something custom myself to learn Rust, and inspecting the serde source is a bit beyond me.

For example, so far I have an enum MyType and my process has been:

  • u32, i32 (and suitable others) are returned as MyType::Int(i64);
  • f32 (and suitable others) as MyType::Float(f32);
  • String (and perhaps suitable others) as MyType::String(&str);
  • and I'd like to return Vec, Hashmap (and suitable others) as some form of reference to a generic collection.

My understanding is that the idiomatic way of representing what I am calling 'collection' classes is through their iterator, but the iterator of Vec and Hashmap are of different concrete types. I think the idiomatic way of representing these is thus a reference to &dyn Iterator<Item::&MyType> through MyType::Iter(&dyn Iterator<Item::&MyType>) [where Iter is one of my MyType enum variants] using the common Iterator trait.

The issue I have is that MyType derives Debug, but because 'dyn Iterator<Item = &MyType>' does not derive Debug`, I get the error mentioned at the start.

I see from playing around that dyn Iterator<Item = &i32> cannot be formatted using {:?} because it doesn't implement Debug, yet a normal iterator of i32 references obviously is Debug, so is there a reason why trait objects cannot?

[Hopefully formatting is ok, this is hopefully my first post of many, so I welcome constructive criticism!]

The correct solution here is probably to not require the iterator to be Debug.

Iterators are not necessarily Debug. For example, this is not Debug:

struct MyIter;

impl Iterator for MyIter {
    type Item = i32;
    
    fn next(&mut self) -> Option<Self::Item> {
        None
    }
}
2 Likes

Hi Alice,

Thank you for the super quick response, I had not thought of that. My c++ brain was thinking that the iterators for Vec, HashMap (and others that I care about) will be Debug, but I forget it has to be true for all cases with Rust.

Now you have set out your example I understand that dyn Iterator is all possible iterators that iterate over my named class, and they could be like your MyIter. Is there a way of restricting to iterators where they implement Debug (as I would be happy with that constraint in this instance), or perhaps that is the issue here Tracking issue for RFC 2289, "Associated type bounds" · Issue #52662 · rust-lang/rust · GitHub.

Reading your motivation more closely, I think the real answer here will be to define your own trait instead of reusing iterator. You can have Debug be a sub-trait of your custom trait.

2 Likes

This can be done:

use core::fmt::Debug;

trait DebugIterator: Iterator + Debug {}
impl<T: Iterator + Debug> DebugIterator for T {}

fn main() {
    let mut it = [1, 2, 3].iter();
    let it: &mut dyn DebugIterator<Item = &i32> = &mut it;
    dbg!(&it);
    for x in it {
        println!("{x}");
    }
}
[src/main.rs:9] &it = Iter(
    [
        1,
        2,
        3,
    ],
)
1
2
3
2 Likes

Aha, thanks both.

This makes sense now. The missing step being to derive my custom iterator for the types I need. A bit more boilerplate than I was expecting, but I'll go with this for now and reach for macros if I find I need a more succinct solution to implementing these.

I would mark both posts as [contributing to] the solution, but going with kpreid.

What an amazing community to respond so quickly to this!

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.