How 'struct' stores one closure which references 'self' inside?

As the following code:

pub struct A<'a>
{
    pub f : Box<dyn Fn() + 'a>
}

impl<'a> A<'a>
{
    fn new ()->A<'a>
    {
        A
        {
            f : Box::new(&||
                {
                })
        }
    }
    fn add(&mut self)
    {
        self.f = Box::new(&||
        {
            self.show();
        });
    }
    fn show(&self)
    {
        println!("AAAA");
    }
}
fn main() {
    let a = A::new();
    a.add();
    (a.f)();
}

and the result is:

    error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src\main.rs:19:28
   |
19 |           self.f = Box::new(&||
   |  ____________________________^
20 | |         {
21 | |             self.show();
22 | |         });
   | |_________^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 17:5...
  --> src\main.rs:17:5
   |
17 | /     fn add(&mut self)
18 | |     {
19 | |         self.f = Box::new(&||
20 | |         {
21 | |             self.show();
22 | |         });
23 | |     }
   | |_____^
   = note: ...so that the types are compatible:
           expected &&mut A<'a>
              found &&mut A<'a>
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 6:6...
  --> src\main.rs:6:6
   |
6  | impl<'a> A<'a>
   |      ^^
   = note: ...so that the expression is assignable:
           expected std::boxed::Box<(dyn std::ops::Fn() + 'a)>
              found std::boxed::Box<dyn std::ops::Fn()>

The problem is that closure should not outlive 'self', but if it references 'self' inside, 'self' should not outlive closure, which is inconsistent. Is there any way to deal with it?

You are trying to define a self-referential struct.

Basically you are trying to define:

struct A<'a> {
    self_ref: Option<&'a Self>,
}

impl<'a> A<'a> {
    fn new () -> Self
    {
        Self { self_ref: None }
    }

    fn add (self: &'_ mut Self)
    {
        self.self_ref = Some(self);
    }
}

which leads to your error: Playground.

Solution

Generally, this is a bad pattern in Rust, which ought to be solved by refactoring your struct into split borrows.

For your closure, for instance, you could have:

struct A {
    f: Box<dyn Fn(&'_ Self)>, // give self as parameter rather than a capture
}

impl A {
    fn new () -> Self
    {
        Self { f: Box::new(|_| {}) } // btw, no need to prefix the closure with `&`
    }

    fn add (self: &'_ mut Self)
    {
        self.f = Box::new(|this| this.show());
    }

    fn show (self: &'_ Self)
    {}
}

and then use it as (self.f)(&self).

  • If you want some sugar, you can define fn f(&self) { (self.f)(self) } and then you can just do self.f()

Other solutions (forcing the self-referential pattern)

One is to solve the mismatch in lifetimes by replacing the elided lifetime '_ in fn add (self: &'_ mut A<'a>) by the lifetime 'a carried by the struct (and replace mut with interior mutability). This will make this code compile, but it will restrict the lifetime of the borrow so much at call site that your struct will be unusable: it is the code using it that will likely fail to compile!

The other option is to stop using compile-time-checked references (since the acyclic graph nature of its analysis does not deal well with self-references since that's, well, a cycle), and use runtime checked references, such as Rc:

use ::std::{
    cell::Cell,
    rc::{self, Rc},
};

struct A {
    self_ref: Cell<Option< RcA >>,
}

#[derive(Clone)]
struct RcA /* = */ (
    Rc<A>,
);

impl RcA {
    fn new () -> Self
    {
        Self(Rc::new(
            A { self_ref: Cell::new(None) }
        ))
    }

    fn add (self: &'_ Self)
    {
        self.0.self_ref.set(Some(
            self.clone()
        ));
    }
}

This compiles and works fine, BUT:

it leaks memory

Indeed, we have created an Rc cycle (yep, cycles are annoying even for some runtime constructs).

Luckily, we can remove the cyclic relationship by introducing a hierarchy between the references: the "back ref" (self_ref) should not own the A it points to. This can be expressed with the rc::Weak reference type:

use ::std::{
    cell::Cell,
    rc::{self, Rc},
};

struct A {
    self_ref: Cell<Option< rc::Weak<A> >>,
}

#[derive(Clone)]
struct RcA /* = */ (
    Rc<A>,
);

impl RcA {
    fn new () -> Self
    {
        Self(Rc::new(
            A { self_ref: Cell::new(None) }
        ))
    }

    fn add (self: &'_ Self)
    {
        self.0.self_ref.set(Some(
            Rc::downgrade(&self.0)
        ));
        
        // upgrading self_ref to a RcA on usage is done with:
        // `RcA(self.0.self_ref.upgrade().unwrap())`
    }
}

Now we can apply this knowledge to your self-referential-through-closure-capture type:

use ::std::{
    cell::Cell,
    rc::Rc,
};

struct A {
    f: Cell< Box<dyn Fn()> >, // idea: it can capture `rc::Weak<A>`
}

#[derive(Clone)]
struct RcA /* = */ (
    Rc<A>,
);

impl RcA {
    fn new () -> Self
    {
        Self(Rc::new(
            A { f: Cell::new(Box::new(|| println!("new()"))) }
        ))
    }

    fn add (self: &'_ Self)
    {
        let this = Rc::downgrade(&self.0);
        self.0.f.set(Box::new(move || {
            let this = RcA(this.upgrade().unwrap());
            this.show();
        }));
    }
    
    fn show (self: &'_ Self)
    {
        println!("Showing!");
    }
    
    fn f (self: &'_ Self)
    {
        let f = self.0.f.replace(Box::new(|| {}));
        f();
        self.0.f.set(f);
    }
}

fn main ()
{
    let a = RcA::new();
    a.f();
    a.add();
    a.f();
}

Note: ideally this forced pattern could be made smoother once we are allowed to write: self: &'_ Rc<Self> receivers, since that would not involve the newtype wrapper (or using a helper trait; those are the two workarounds for &'_ Rc<_> receivers).

4 Likes

Great! For the first solution, i make a test with keyword "Self" in trait as you said "give self as parameter rather than a capture" and it works:

Note: the above code is the abstraction of some logic~~
For the reason not pass parameter to the closure, i want call the add method and then call impl_add
inside through trait object to add the closure.I did not known that 'Self' could be used in trait(In cpp, 'Self' works as a template parameter and for parameter f:Box<dyn Fn('_ Self)>, it could be different type with different 'Self', i thought it would break the dynamic dispatch. Now it is clear).

But the first solution doest not work with following code for type f:Box<dyn Fn(&'_ mut Self)>:

It will lead to mut borrowing and borrowing at the same time.

The type Self is, within a given scope, statically known to be the type that the scope applies to: in an impl block, Self is the type the impls are for; in a struct definition the type, it is the type of the struct (Self = A). In case of doubt you should be able to replace Self with the actual type.

There is one "exception": traits. Here Self must be seen as a hidden generic parameter <Self> that will indeed be replaced by the type that implements the trait when the trait is used / referred to (e.g., it will be the Self of an impl block when defining it, but when using a method .method(), which is sugar for <TypeThatImplementsTrait as Trait>::method, then Self will be TypeThatImplementsTrait.

Which means that if you are using dynamic dispatch and thus trait objects (dyn TA in your example),
when doing some_dyn_Ta_thingy.method() you are actually doing <dyn TA as TA>::method(...) meaning that Self = dyn TA.

I did not know that you intended to have an impl_add function interacting with trait objects. In that case, you should:

  • "disable" impl_add from being dynamically dispatched (by adding a where Self : Sized bound on the method definition,

  • (or you could change the &'_ Self parameter of the closure to be a &'_ dyn TA parameter, but that would lead to many more code complications)

else you'll not be able to use dyn TA: Playground


Yes, that's where you'll need disjoint borrows (there are many topics in this forum discussing this), else you will be potentially giving &mut access to self.f within the closure body, which violates the exclusivity premise of &mut; split your struct fields into .f and the other ones packed in a substruct, and have your core logic work with the substruct itself (and, if needed, delegate from the initial struct to this substruct through Deref or manually). And then the call becomes: (self.f)(&mut self.fields)

  • (at which point you can even use FnMut rather than just Fn)
3 Likes

Emm, for your solution combining 'Rc' and 'Cell', i tried some other ways without 'RcA', but failed. In your solution, struct A becomes a substruct and the behaviors of it are binded to RcA. It is a nice solution (Hmm, a litte noise for wrapping) . As you said, my original implementation is a bad design pattern:

this is a bad pattern in Rust

I will move the closure outside the struct.
Thanks help!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.