Rust Generics: Why does the function signature compile but T::default() inside the body fails?

As shown in the code, I have not applied any explicit constraints to the generic parameter T. However, the parameter's type, MyBuffer<T>, requires the structural bounds T: Default + 'static in its definition.

  1. This code is accepted by the compiler (the function signature compiles).
  2. But if I add the line T::default() to the function body, the code fails to compile.

My question is:

What is the specific Rust rule that governs this behavior?

Edition: Pls ignore the following detail about Default, because I messed it up when trying to find an explanation, see the real code at the 5th replying-post.

If the function's generic parameter T truly has no explicit bounds, why does the compiler accept the function signature where T appears to violate the structural constraints required by MyBuffer<T>? (i.e., Why is the Well-Formedness Predicate not immediately enforced on the signature?)

Conversely, if the compiler has successfully determined the necessary bounds for T (i.e., T: Default), why does the method call T::default() fail to compile inside the function body? (i.e., Why are the structural bounds not imported into the function body's scope for Trait resolution?)

struct MyBuffer<T: Default + 'static> {
    [...]
}


fn alloc_clone<T>(
    pined: &mut Pin<&'static mut BufList>,
) -> Result<Pin<&'static mut MyBuffer<T>>, bool> {
     [...]
}

Can you show the declarations that establish structural constraints on MyBuffer<T>'s T parameter?

I suspect they won't change the answers much, but it might help some of us understand what you're trying to do.

Added at the origin posting. I can figure out maybe a function defination only cares for the code inside the body, ignoring the bounds on its parameters.

While I need a more detailed explanation to make it more clearly

For me it doesn't, with exactly the error one might expect:

error[E0277]: the trait bound `T: Default` is not satisfied
 --> src/lib.rs:9:30
  |
9 | ) -> Result<Pin<&'static mut MyBuffer<T>>, bool> {
  |                              ^^^^^^^^^^^ the trait `Default` is not implemented for `T`
  |
note: required by a bound in `MyBuffer`
 --> src/lib.rs:4:20
  |
4 | struct MyBuffer<T: Default + 'static>(PhantomData<T>);
  |                    ^^^^^^^ required by this bound in `MyBuffer`
help: consider restricting type parameter `T` with trait `Default`
  |
7 | fn alloc_clone<T: std::default::Default>(
  |                 +++++++++++++++++++++++

Sorry, my mistake.
I modified the code trying find some explanation but did not compile it again.

the real code is

struct MyBuffer<T: 'static> {
    [...]
}


fn mbuf_singleton<'a, T>(buf: Pin<&'a mut MyBuffer<T>>) -> Option<Pin<&'a mut MyBuffer<T>>> {
    return None;
}

I am checking the 'static bounds, but add the Default for testing and messed it up.

In my understanding, 'a does not have to be 'static, so there is no implict indication T: 'static; But MyBuffer<T: 'static> explicits requiring this.

I don't know why the code is accpeted. (This time I compiled the code to avoid more mistake)

The bounds on the struct declaration aren't implied at every use place of the struct.

You have to write:

fn foo<T: Default>() {}

Not just write the bound at the struct declaration site. In fact it's often considered bad practice to put bounds there.

1 Like

The question is I have to repeating T: Default, but I don't have to repeating T: 'static.
What makes the difference?

Lifetime bounds on struct generics are implied elsewhere; non-lifetime bounds are not.

Making more bounds implied is not a slam-dunk as it takes away flexibility from library authors if there is not an opt-in, and introduces a SemVer hazard. Here's an article about that.

3 Likes

Since then, but why Lifetime bounds allows that, It seems lifetime bounds having same problem as non-lifetime bounds;

In my previous example , seems T: 'static was brought into mbuf_singleton, but it also shows this bringing does not occure everywhere, for example:

struct BufOps<T: 'static> {
    clone: fn(mbuf: Pin<&mut MyBuffer<T>>) -> Option<Pin<&mut MyBuffer<T>>>,
    free: fn(mbuf: Pin<&mut MyBuffer<T>>),
}

I can't delete T: 'static bounds although the paramters of the function pointer indicating that

You use to always have to explicitly state lifetime bounds on the struct definition itself, even if implied by fields. RFC 2093 made it so most lifetime bounds required for the struct definition to compile are inferred.

// It use to be that this definition would error and require you to explicitly
// state that `T: 'a` in the header: `struct Foo<'a, T: 'a> { ... }`.
//
// Today, the `T: 'a` is inferred by the presence of the field.  (Mostly:
// see the "n.b." below.)  And, like when you have an explicit bound,
// `T: 'a` is implied elsewhere (such as when `Foo<'a, T>` is present
// in a function input argument).
struct Foo<'a, T> {
    field: &'a T,
}

However, during stabilization of the RFC feature, it was decided to exclude 'static specifically, as there was a feeling that it might be confusing for 'static only... apparently.[1] Personally, I also find it more confusing that the bounds are not inferred for 'static only, since it is inconsistent. :person_shrugging:

(N.b. If T: ?Sized, there is still a difference between inferred and explicit bounds on your struct definitions, and you probably want explicit.)


  1. you can link chase from here â†Šī¸Ž

1 Like

Something is not very clear.

If it is true:

T: 'static was brought into mbuf_singleton

Notice it is T: 'static, too.

And, for

struct MyBuffer<T: 'static> {
     a: T
}

struct BufOps<'a, T: 'a> {
    clone: fn(mbuf: Pin<&'a mut MyBuffer<T>>) -> Option<Pin<&'a mut MyBuffer<T>>>,
}

still failed, shows Foo<'a, T> implicits T: 'a is unrelated with this issue.

It seems just a rule:

fn F<T>(mbuf: Pin<&mut MyBuffer<T>>)

does not require it to be writen as

fn F<T: 'static>(mbuf: Pin<&mut MyBuffer<T>>)

But struct defination is required:

struct BufOps<T: 'static> {
    clone: fn(mbuf: Pin<&mut MyBuffer<T>>) -> Option<Pin<&mut MyBuffer<T>>>,
}