Can't use chain iterator inside quote! macro

I'm trying to use a chain iterator inside a quote! macro, but the compiler tells me that the types are mismatched: expected struct syn::punctuated::Iter, found struct std::iter::Chain.

use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, DataEnum, DeriveInput, parse_macro_input};

trait NextEnumVariant {
	fn next_variant(&self) -> Self;
}

#[proc_macro_derive(NextEnumVariant)]
pub fn next_enum_variant(input: TokenStream) -> TokenStream {
	let DeriveInput {data,ident,..} = parse_macro_input!(input);
	match data {
		Data::Enum(DataEnum{variants,..}) => {
			let iter = variants.iter();
			let mut shifted_iter = variants.iter();
			shifted_iter = shifted_iter.chain(shifted_iter.next()); //here's where the error happens
			quote!(
				impl NextEnumVariant for #ident {
					fn next_variant(&self) -> Self {
						match *self {
							#(#iter => #shifted_iter),* //here's where I'm trying to use the iterator
						}
					}
				}
			).into()
		}
		Data::Struct(_) => panic!("This macro doesn't work for structs"),
		Data::Union(_) => panic!("This macro doesn't work for unions"),
	}
}

Is there something I'm doing wrong? If not, is there some other way I could makes something like shifted_iter?

If two iterators iter1 and iter2 have type T and U, then iter1.chain(iter2) has type Chain<T, U>, and not T or U. Think about it: if the new iterator has different behavior and additional state (like chaining has to have!), how could it be the same type as either of the iterators it was created from? It necessarily is a distinct type. But you are trying to assign it back to one of the iterators, so that's a type mismatch.

It's also quite unidiomatic to create a mutable variable and re-use that same variable for a different purpose. And .chain(the_same_iterator.next()) is especially weird. What are you actually trying to do? I can't understand what's the intention of that line you marked.

Thank you, after reading your comment I realized that I should be binding, instead of assigning, to shifted_iter. I haven't worked too much with iterators in this way, so please forgive my mistake.

Also, just to clear up what I was trying to do: I wanted to 'shift' all the elements of the iterator to the left, so if iter was 1 2 3, shifted_iter would be 2 3 1.

Then you can just take and drop the appropriate items explicitly, no need for anything complicated:

let first = variants.iter().take(1);
let shifted_iter = variants.iter().skip(1).chain(first);

That's perfect, thank you!

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.