Why does this not compile? Box<T>::Target != T?

This code compiles fine:

use std::ops::Deref;

struct Data {
    boxed: Box<&'static i32>

impl Data {
    fn use_data(&self, user: impl for <'a> FnOnce(&'a i32)) {

However, modifying the definition of user with this will cause the compilation to fail:

user: impl for <'a> FnOnce(<Box<&'a i32> as Deref>::Target)
// instead of   for <'a> FnOnce(&'a i32)

This does not make sense to me as it is my understanding that <Box<T> as Deref>::Target should be identical to T. What am I missing in this example?

1 Like
    fn use_data(
        user: impl for<'a> FnOnce(<Box<&'a i32> as Deref>::Target) + std::ops::FnOnce(&i32),
    ) {

Why is it necessary to add that? The example I gave is being used in a context where the T of Box<T> won't be known ahead of time so I can't manually add the + FnOnce(&i32). I want to understand why <Box<T> as Deref>::Target is not equivalent to T in the first place.

Without high rank trait bounds it equivalent:

fn use_data<'a>(&self, user: impl FnOnce(<Box<&'a i32> as Deref>::Target)) {


Yeah, that seems like it should be equivalent. I've opened an issue.

Upon researching more, it seems 'a as a template parameter is not equivalent to for<'a>. The template form means that the function has to work for a specific lifetime 'a which is specified when the function is called. The for form is a higher-rank trait bound, meaning the function has to work for any possible lifetime 'a, you cannot pass a function which will only work for a specific lifetime.

I'm still uncertain as to why the original discrepancy exists though, as both versions use for<'a>.


You are hitting a current limitation of the type-checker when dealing with higher-order lifetimes. Very annoying limitation indeed.

I expect this will be "fixed" / supporting this will be implemented by the time GATs want to go stable, since it will otherwise be quite frustrating to work with them.