I have a struct definition which includes, among other things, this field:
pub struct Separated<'a, I, T>
{
..., // other fields,
separated: NonNull<dyn 'a + Iterator<Item = T>>,
}
Shortly after, in its constructor, I attempt to initialize that field as a dangling pointer:
let sep = Separated {
..., // other fields
separated: NonNull::dangling(),
};
This, weirdly, produces this error:
error[E0282]: type annotations needed
|
16 | separated: NonNull::dangling(),
| ^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T`
There's nothing mysterious about that field; its type is explicitly set in the struct definition. I do not understand why the type inferencer cannot infer an appropriate type to inject there.
A minimal 20-line example producing this error can be found below and on the playground:
use std::pin::Pin;
use std::ptr::NonNull;
pub struct Separated<'a, T> {
t: &'a T,
separated: NonNull<dyn 'a + Iterator<Item = T>>,
}
impl<'a, T> Separated<'a, T>
where
T: 'a + Copy + PartialEq,
{
fn new(t: &'a T) -> Pin<Box<Self>> {
let sep = Separated {
t,
separated: NonNull::dangling(),
};
unimplemented!()
}
}
I do need separated
to be a pointer to a trait object instead of a monomorphized type: the real trait object which it will contain is composed of a bunch of iterator combinators, including ones like Map
and TakeWhile
, whose types include function pointers and are therefore unnameable.
NonNull::dangling
is not a parametric function: the NonNull<T>
struct is parametric, but this function is not. Therefore, I can't just turbofish my way out of this. I'm not sure how I'd go about providing type annotations at all.
Context, if useful: the whole reason I'm going down this path is I am attempting to create an iterator combinator, auto-implemented for all appropriate iterators, which injects a single element between each N elements of the origin iterator. It's not all that hard to accomplish for a single iterator, but much harder to do as a generic combinator, because the IntoChunks
struct produced by Itertools' chunks()
combinator is not itself an iterator, just a struct implementing IntoIterator
. Therefore, we need to keep track of the IntoChunks
struct as well as the iterator it produces.
The approach I'm taking is to create a self-referential struct, Separated
, which contains both of those. This should be safe assuming the struct is always pinned. I then impl Iterator for Separated
and just defer the next
calls to self.separated
.
According to the standard docs, the definition of NonNull::dangling()
is this:
impl<T> NonNull<T> { pub const fn dangling() -> NonNull<T> { /* ... */ } }
And in your code, you're using it in a place where an expression with the type NonNull<dyn 'a + Iterator<Item = T>>
, so the return value must have this type.
The subtle thing here is that generic type parameters have an implicit Sized
bound (unless it has a ?Sized
bound). So because the implementation of NonNull::dangling
does not have a ?Sized
bound, Rust will attempt to infer the type parameter of NonNull
based on these requirements:
NonNull::<T>::dangling()
method does not have a bound T: ?Sized
, it is only implemented for sized types T
, and the type parameter must be sized.dyn 'a + Iterator<Item = T>
.However, because trait objects ("dyn Trait
types") are unsized, it is impossible for Rust to satisfy both requirements at once, hence why it "cannot infer type for type parameter T
".
In fact, by explicitly adding the type to your playground example, you'll get a different error message that's more explicit about the problem:
let sep = Separated::<'a, T> {
t,
separated: NonNull::<dyn 'a + Iterator<Item = T>>::dangling(),
};
error[E0599]: no function or associated item named `dangling` found for type `std::ptr::NonNull<(dyn std::iter::Iterator<Item = T> + 'a)>` in the current scope
--> src/lib.rs:16:64
|
16 | separated: NonNull::<dyn 'a + Iterator<Item = T>>::dangling(),
| ^^^^^^^^ function or associated item not found in `std::ptr::NonNull<(dyn std::iter::Iterator<Item = T> + 'a)>`
|
= note: the method `dangling` exists but the following trait bounds were not satisfied:
`dyn std::iter::Iterator<Item = T> : std::marker::Sized`