Strange compiler behavior with `'static + Sized` boundary

I am not good at English. Sorry if there are any funny expressions.
Additionally, the title of this topic may not be appropriate, as I am just guessing this is probably relevant.

Anyway, please see the code below.

In this code, I don't understand why there are no compilation errors with test3. It seems to me that the problematic part of test2 has not been fixed.

What is going on in test3?

fn main() {}

trait SomeTrait {
    fn method(&self) {}
}
 
impl<T: 'static + Sized> SomeTrait for T {
    // NOP.
}

// ✅ No Compile error, and I can understand this👌.
fn test1<T: 'static + ?Sized>(arg: &'static Box<T>) {
    arg.as_ref().method();
}

/* ❌ Compile error, but I can understand this👌.
The error says "error[E0521]: borrowed data escapes outside of function".
Maybe the value obtained by traversing `arg` does not satisfy the `static`
boundary required from blanket implementation. */
fn test2<T: 'static + ?Sized>(arg: &Box<T>) {
    arg.as_ref().method();
}

/* ✅ No Compile error, but I can't understand this💦.
The only difference with `test2` is the difference between `?Sized` or `Sized`,
and I don't think it resolves what the error was pointed in `test2`. */
fn test3<T: 'static + Sized>(arg: &Box<T>) {
    arg.as_ref().method();
}

I suspect that the type inference on as_ref may be confusing things. It would help to specify the type that you expect from the as_ref call.

2 Likes

I think what's happening here is that after .as_ref() you get an &T. When you call method the compiler tries to first check if T: SomeTrait, but it fails because T: ?Sized, and the impl for SomeTrait requires T: Sized. So it falls back to calling .method() on the reference itself, i.e. it tries to check if &T: SomeTrait, but this fails because the implementation requires 'static and the reference has a non-'static lifetime. Hence the error about the borrowed data not living long enough.

3 Likes

Thanks for your replies.

I'm still trying to organize my thoughts...

But it seems to me that I only partially understood why test2 caused the error, I guess.

From the points you both made about as_ref, I can summarize my current understanding...

  • There are two interpretations of as_ref.
  • One is as_ref for Box, which fails in test2 with Sized boundary of the blanket implementation.
  • One is as_ref of a reference, which fails in test2 due to lifetime constraints.
  • If both interpretations fail, an error occurs.

Is this correct way of thinking?

No, there's only one .as_ref() [1], but there are multiple ways to call .method(). One tries to consider T the type that should implement SomeTrait (but it fails because a trait bound is missing, thus it leads to the next one) and the other tries to consider &T the type that should implement SomeTrait (which fails too, but with a lifetime error, and this is where the compiler stops)


  1. well, technically there are multiple ways to call .as_ref() too, but the first one succeed so the others are not considered. It also wouldn't change anything for your error. ↩︎

1 Like

Thanks to your help, I now understand.

Normally, the previous explanation would have been enough... However, I carelessly forgot something very basic (&self with dot operator can accept non-referential types). I’m embarrassed.

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.