Cannot infer type for type parameter T, when the type is explicitly specified in a struct definition

coriolinus picture coriolinus · Feb 29, 2020 · Viewed 8.6k times · Source

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.

Answer

Frxstrem picture Frxstrem · Feb 29, 2020

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:

  • Because the 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.
  • The type parameter must be 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`