Why do I have to fight the borrow checker here?

#[derive(Default)]
struct State<'a> {
    clickable: Option<&'a dyn Clickable>,
}

trait Clickable {
    fn click(&self);
}

trait StatefulWidget {
    type State: ?Sized;
    
    fn render(self, state: &mut Self::State);
}

#[derive(Default)]
struct MyWidget;

impl<'a> StatefulWidget for MyWidget {
    type State = State<'a>; // the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
    
    fn render(self, state: &mut Self::State) {
        // bla bla
    }
}

fn main() {
    let mut state = State::default();
    MyWidget::default().render(&mut state);
}

Why do I have to add lifetimes either to the StatefulWidget or MyWidget, just to have associated type with a lifetime?
Why do I have to do PhantomData with a lifetime bullshit, and deal with what comes with that, just to have dyn Clickable on the stack?

You have added a generic parameter (a lifetime parameter) to State, and this needs to be provided. That's what the trait implementation is doing, it doesn't mean that you added a lifetime parameter to the trait itself.

the problem is when rust tries to find a implementation for a specific trait it goes through all impl blocks with all theoretically possible generic parameters
(well it does something logically equivalent, what i just described would take enough energy to boil the oceans, but lets ignore that)

for example if i have
impl<T> From<T> for Foo

and i write Foo::from(20i32)
the compiler would first generate the bound, Foo: From<i32>, then go through all the impls:
T = i128, Foo: From<i128>: WRONG
T = String, Foo: From<String> : WRONG
T = UdpSocket, from = Foo: From<UdpSocket> : WRONG
...
T = i32, Foo: From<i32> : RIGHT
...
and selects the implementation that was right, in this case there is only one such implementation, so the compiler is sound.

now lets imagine that your impl was accepted, and the compiler tries to prove the bound MyWidget: StatefulWidget, there are infinite lifetimes, and all lifetimes result in a implementation of the trait, so going through all the impls would look like this:
'a = 'static, MyWidget: StatefulWidget: RIGHT
'a = '1, MyWidget: StatefulWidget: RIGHT
'a = '2, MyWidget: StatefulWidget: RIGHT
...
'a = '∞, MyWidget: StatefulWidget: RIGHT

and now the compiler has an infinite amount of implementations to choose from, all of which are valid, so everything catches on fire and the sky turns green.

Why do I have to do PhantomData

i do not see any PhantomData in the example, i assume you did something like

struct MyWidget<'a> {
    _marker: PhantomData<&'a ()>,
}

impl<'a> StatefulWidget for MyWidget<'a> {
...
}

that technically works, as all MyWidget now have a specific lifetime, and the implementation of StatefulWidget is for that lifetime, but it is too restrictive, and unlikely to do what you want (MyWidget would now be very difficult to move around or use with different states)

what you actually want is GATs, generic associated types:

trait StatefulWidget {
    type State<'a>: ?Sized;

    ...
}

impl StatefulWidget for MyWidget {
    type State<'a> = State<'a>;

    ...
}

the difference is that now there is not an infinite amount of implementations, there is a finite amount (1), but each implementations has a infinite amount of Sate associated types, one for each lifetime.

depending on how the structs borrow from each other you may also want to restrict State further:

type State<'a>: ?Sized
where
    Self: 'a,
;

but that depends on your codebase, so i do not know if its correct.

have you considered

trait StatefulWidget {
    type State<'a>: ?Sized;
    
    fn render(self, state: &mut Self::State<'_>);
}

? it may not be possible, but if it is it's probably ideal.

note that you can still write

struct Other;

impl StatefulWidget for Other {
    type State<'a> = String;
    
    fn render(self, state: &mut Self::State<'_>) {
        // bla bla
    }
}

with the new trait.
this is just a way to say : "State may not be just a type, but may also be a family of types bound by a lifetime parameter"

That works. Unfortunately the StatefulWidget isn't from my own codebase, but from a library. I guess I'll just have to make my own struct, and then make my own traits, and then my own functions, because the libraries' can't recognize the new ones... Anyways, I'll just Box it and call it a day. But thanks for taking your time and helping me out.

if render does take self by value like you showed i would definitely use PhantomData, that seems like the simplest fix

You might want to implement StatefulWidget on &'a MyWidget then.

Section with attempts to explain "why" but without practical suggestions on fixing the OP

The language requires an associated type be fully determined by it's path...[1]

<SomeImplementor<'maybe, With, Generics> as Trait<'more, Gen>>::Assoc<'x, Y>

...which for your OP would be...

<MyWidget as StatefulWidget>::State

...and as 'a doesn't appear in the path, it cannot appear in the associated type. There are a number of reasons, including

  • There's generally no way at the use site to know where the lifetime should come from (imagine a generic context).
  • If everything in the path fulfills a 'static bound, the language can and does assume that the associated type also fulfills a 'static bound. For example:
    // This compiles but wouldn't be sound if `State = State<'non_static>`
    // was possible for `T: 'static` (like `MyWidget` is).
    fn example<T>(state: T::State) -> Box<dyn Any>
    where
        T: 'static + StatefulWidget<State: Sized>,
    {
        Box::new(state)
    }
    

Owned unsized values on the stack aren't yet supported. The borrowed forms either have lifetimes so that Rust can uphold it's soundness guarantees, or don't have lifetimes but require the use of unsafe (and you uphold the soundness requirements yourself).

I'm not sure how much owned unsized values would help you anyway -- Option<T> doesn't support unsized T. You would need some other approach. On top of that, the trait doesn't need them (it takes a borrow of Self::State).


If you really need this for MyWidget (if &'a MyWidget isn't viable), you could perhaps do something like

#[derive(Default)]
struct NoClicks;

impl Clickable for NoClicks {
    fn click(&self) {}
}

// Now instead of `None`, use `&NoClicks [as &dyn Clickable]`

And if you still need State<..> as a separate type for whatever reason, you could then

#[derive(Default)]
struct State<T: ?Sized> {
    clickable: T,
}

impl StatefulWidget for MyWidget {
    type State = State<dyn Clickable>;
    ...
}

// ...
    let mut state = State::<NoClicks>::default();
    MyWidget::default().render(&mut state);

(Or maybe just have State = dyn Clickable.)


  1. that is, only generic lifetimes or generic types that are part of the path can be used in the associated type ↩︎

Apparently doing

impl StatefulWidget for MyWidget {
    type State = State<'static>;
    
    fn render(self, state: &mut Self::State) {
        // bla bla
    }
}

works. Why? I have no idea. What does 'static means in this context? I have no idea.
They have like 3 or so definitions for 'static in their documentation. I don't think neither of them applies for this one.

Is this what it's like to program on Rust? Just huffing on compiler vibes more than any vibe coder will ever vibe on their slop?

'static' roughly means "a lifetime that will outlive all other non-static lifetimes" or in other words "this struct does not actually have any lifetimes",
for example any i32 is 'static, it doesn't matter what you do with it, where you move it, it will never not live long enough (as it doesn't have any lifetimes to begin with).

by saying State<'static> you are saying that State does not contain any non-static references (as any non-static reference would not outlive 'static, by definition.)

this makes it impossible to use your widget implementation with a &dyn Clickable living on the stack, as whatever lifetime it has will not be 'static, as a reference to a local variable can't outlive the function (and is thus non-static),

even storing &dyn Clickable in a box on the heap doesn't work anymore, as references to the contents of boxes are not 'static either, the only way to get 'static &mut T in std without unsafe is Box::leak (and please do use that unless you know you have a very good reason)
(technically you can ceorce ! to literally anything, but the entire point of ! is that it can never be created, so it won't help in this case)

Is this what it's like to program on Rust? Just huffing on compiler vibes

no, rust programmers generally seek to understand why something does/doesn't work, the rust compiler has the best and most helpful error/warning messages out of any compiler i have ever worked with.

That's the thing.
Currently I have State with <'a> lifetime,
with the field:

popup_text: &'a str,

It's the only field that uses the lifetime.
And it works with 'static in the implementation.
I can change the popup_text just fine.

I think 'static works here simply as shut up compiler.

is pop_text a string created directly (e.g. State { popup_text: "foo" }), if it is then "foo" is stored in static memory and thus 'static, which would make 'a equal to 'static.

if your intention is to only store static data then you can remove all the lifetimes and just write popup_text: &'static str, just note that you won't be able to borrow Strings and other text sources for popup_text if you do that (which is also the case with your current MyWidget implementation).

Oops, changing the field wasn't the problem. Problem was that when the function accepted both the state and the widget it'd need to satisfy both of them to share the same lifetime. Which for some reason broke during the loops, compiler thinking that multiple iterations equals multiple borrows, which I guess makes sense. But simply doing 'static in the implementation -- it simply works without any problems.

Specifically with this function signature

pub fn render_stateful_widget<W>(widget: W, state: &mut W::State)
where
    W: StatefulWidget,

And yeah, you're right about rvalue static promotion. I tested it just now.

The solution could be any of

impl StatefulWidget for MyWidget {
    type State = State<'static>;
    
    fn render(self, state: &mut Self::State) {
        // bla bla
    }
}

If you're State lifetimes are 'static.
As suggested by me.

Or

By doing this, if you own the trait (StatefulWidget)

trait StatefulWidget {
    type State<'a>: ?Sized;
    ...
}

As suggested by @miro.

Or

By doing

impl<'a> StatefulWidget for &'a MyWidget {
    type State = State<'a>;
    
    fn render(self, state: &mut Self::State) {
        // bla bla
    }
}

If you're fine with both MyWidget and State sharing the same lifetime.

As suggested by @ProgramCrafter