I'm making an async function for a library. It's not very big, but yet I'm meeting the error:
error: reached the type-length limit while instantiating `std::pin::Pin::<&mut std::future...}[1]::poll[0]::{{closure}}[0])]>`
|
= note: consider adding a `#![type_length_limit="9479903"]` attribute to your crate
error: aborting due to previous error
I have asked around and it seems nobody knows how to overcome this and only suggest that I do as the error suggests. But that makes for a long compile time; and I suppose it also requires the type_length_limit in any crate that uses my library.
I really want to get to the bottom of what part of my code is the 'bottleneck'.
In the linked-to commit I have adjusted the type length limits to be just enough to compile the tests (cargo test). It required a limit of 10 million. How can it possibly get this high? I know it's the return type that becomes complex due to combinators and what-not that are applied on the futures in the function but I only have some ten-fold of combinators/wrappers, so there must be some exponential growth at play here?
error: reached the type-length limit while instantiating `std::pin::Pin::<&mut std::future...}[1]::poll[0]::{{closure}}[0])]>`
|
= note: consider adding a `#![type_length_limit="18960398"]` attribute to your crate
It almost doubled from 10 million to 19 million just because I now call the problematic async function from inside an async function. How is that possible? Is it a bug?
Expanding on @alice with an example, I recently hit this type length limit issue working with async/await and streams. I specifically was doing this:
let stream = stream::iter(vec![1,2,3]);
let stream_senders_fut = stream.for_each_concurrent(3, |num| async move {
do_async_stuff(num).await;
// I didn't need to return anything here in this example
});
...and got "error: reached the type-length limit while instantiating XXX"
To solve the problem in the simplest way and drastically speed up compile time, I Pin::box'ed the future like this:
let stream = stream::iter(vec![1,2,3]);
// I specify the return type on stream_fut as Pin<Box<...>> while specifying the future Output as () because that is what the future resolves to
let stream_fut: Pin<Box<dyn Future<Output=()>>> = Box::pin(stream.for_each_concurrent(3, |num| async move {
do_async_stuff(num).await;
}));
I could then satisfyingly remove the #![type_length_limit="6954178"] as well as recompile in milliseconds vs minutes.
I hope this helps someone else struggling with this!
Thanks for sharing your experience.
Why not just .boxed() on the future? (what is the difference?)
I solved my problem by putting .boxed() behind a future. Only in one place actually.
the trait `std::marker::Unpin` is not implemented for `dyn core::future::future::Future<Output = ()>`
And the best description I could back up why this error is important is:
The main point made is that, without the pinning constraint, you could safely move the future out of a Box into a new Box and poll it from multiple memory locations, which would be unsound.
I cannot give a real underlying reason as to why your's is not throwing this error when compiling, but maybe a more seasoned pinning/future expert can jump in and explain. My main goal was to give another option to solve this problem and provide a high-level reason as to why it works. Either way, I hope it helps if you end up in the same situation!