pm100
pm100

Reputation: 50200

What is the Rust equivalent of C++'s shared_from_this?

I have an object that I know that is inside an Arc because all the instances are always Arced. I would like to be able to pass a cloned Arc of myself in a function call. The thing I am calling will call me back later on other threads.

In C++, there is a standard mixin called enable_shared_from_this. It enables me to do exactly this

class Bus : public std::enable_shared_from_this<Bus>
{
....
   void SetupDevice(Device device,...)
   {
       device->Attach(shared_from_this());
   }
}

If this object is not under shared_ptr management (the closest C++ has to Arc) then this will fail at run time.

I cannot find an equivalent.

EDIT:

Here is an example of why its needed. I have a timerqueue library. It allows a client to request an arbitrary closure to be run at some point in the future. The code is run on a dedicated thread. To use it you must pass a closure of the function you want to be executed later.

use std::time::{Duration, Instant};
use timerqueue::*;
use parking_lot::Mutex;
use std::sync::{Arc,Weak};
use std::ops::{DerefMut};

// inline me keeper cos not on github
pub struct MeKeeper<T> {
    them: Mutex<Weak<T>>,
}

impl<T> MeKeeper<T> {
    pub fn new() -> Self {
        Self {
            them: Mutex::new(Weak::new()),
        }
    }
    pub fn save(&self, arc: &Arc<T>) {
        *self.them.lock().deref_mut() = Arc::downgrade(arc);
    }
    pub fn get(&self) -> Arc<T> {
        match self.them.lock().upgrade() {
            Some(arc) => return arc,
            None => unreachable!(),
        }
    }
}
// -----------------------------------

struct Test {
    data:String,
    me: MeKeeper<Self>,
}

impl Test {

    pub fn new() -> Arc<Test>{
        let arc = Arc::new(Self {
            me: MeKeeper::new(),
            data: "Yo".to_string()
        });
        arc.me.save(&arc);
        arc
    }
    
    fn task(&self) {
        println!("{}", self.data);
    }

    // in real use case the TQ and a ton of other status data is passed in the new call for Test
    // to keep things simple here the 'container' passes tq as an arg

    pub fn do_stuff(&self, tq: &TimerQueue) {
        // stuff includes a async task that must be done in 1 second

        //.....
        let me = self.me.get().clone();
        tq.queue(
            Box::new(move || me.task()),
            "x".to_string(),
            Instant::now() + Duration::from_millis(1000),
        );
    }


}

fn main() {
    // in real case (PDP11 emulator) there is a Bus class owning tons of objects thats
    // alive for the whole duration
    let tq = Arc::new(TimerQueue::new());
    let test = Test::new();
    test.do_stuff(&*tq);
    // just to keep everything alive while we wait
    let mut input = String::new();
    std::io::stdin().read_line(&mut input).unwrap();
}

cargo toml

[package]
name = "tqclient"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
timerqueue = { git = "https://github.com/pm100/timerqueue.git" }
parking_lot = "0.11"

Upvotes: 2

Views: 1119

Answers (4)

Eloff
Eloff

Reputation: 21688

Since Rust 1.6 you can use Arc::new_cyclic to create an instance of your struct in an Arc, and set a Weak pointer to it inside the struct that you can upgrade to get the Arc back.

use std::sync::{Arc, Weak};

struct MyStruct {
    weak: Weak<MyStruct>,
}

impl MyStruct {
    /// Constructs a reference counted Gadget.
    fn new() -> Arc<Self> {
        Arc::new_cyclic(|weak| {
            // Create the actual struct here.
            MyStruct { weak: weak.clone() }
        })
    }

    /// Returns a reference counted pointer to Self.
    fn arc(&self) -> Arc<Self> {
        self.weak.upgrade().unwrap()
    }
}

Upvotes: 0

c z
c z

Reputation: 9027

Alice's answer seems the most logical.

However, shared_from_this<T> does not use some under-the-hood C++ magic; it is simply a base class with a weak_ptr<T> member. While Rust favours encapsulation and traits over inheritance, there is nothing to prevent us from accomplishing the same.

Unlike self:Rc<T>, this is somewhat verbose due to the lack of inheritance. The use case for this method is that we do not need a reference back to the original Rc, thus there are fewer compile-time constraints and &self may simply be used.

A similar pattern is perhaps more usefully employed by nested child objects to obtain a parent, rather than on a parent to obtain itself.

Implementation:

use std::cell::RefCell;
use std::rc::{Rc, Weak};

pub struct SharedFromThisBase<T> {
    weak: RefCell<Weak<T>>,
}

impl<T> SharedFromThisBase<T> {
    pub fn new() -> SharedFromThisBase<T> {
        SharedFromThisBase {
            weak: RefCell::new(Weak::new()),
        }
    }

    pub fn initialise(&self, r: &Rc<T>) {
        *self.weak.borrow_mut() = Rc::downgrade(r);
    }
}

pub trait SharedFromThis<T> {
    fn get_base(&self) -> &SharedFromThisBase<T>;

    fn shared_from_this(&self) -> Rc<T> {
        self.get_base().weak.borrow().upgrade().unwrap()
    }
}

Contrived example:

struct MyStruct {
    base: SharedFromThisBase<MyStruct>,
}

impl SharedFromThis<MyStruct> for MyStruct {
    fn get_base(&self) -> &SharedFromThisBase<MyStruct> {
        &self.base
    }
}

impl MyStruct {
    fn new() -> Rc<MyStruct> {
        let r = Rc::new(MyStruct {
            base: SharedFromThisBase::new(),
        });
        r.base.initialise(&r);
        r
    }

    pub fn hello(&self) {
        println!("Hello!");
    }
}

fn main() {
    let my_struct = MyStruct::new();
    let my_struct_2 = my_struct.shared_from_this();
    my_struct_2.hello();
}

Upvotes: 2

pm100
pm100

Reputation: 50200

having found that I needed this three times in recent days I decided to stop trying to come up with other designs. Maybe poor data design as far as rust is concerned but I needed it.

Works by changing the new function of the types using it to return an Arc rather than a raw self. All my objects are arced anyway, before they were arced by the caller, now its forced.

mini util library called mekeeper

use parking_lot::Mutex;
use std::sync::{Arc,Weak};
use std::ops::{DerefMut};


pub struct MeKeeper<T> {
    them: Mutex<Weak<T>>,
}

impl<T> MeKeeper<T> {
    pub fn new() -> Self {
        Self {
            them: Mutex::new(Weak::new()),
        }
    }
    pub fn save(&self, arc: &Arc<T>) {
        *self.them.lock().deref_mut() = Arc::downgrade(arc);
    }
    pub fn get(&self) -> Arc<T> {
        match self.them.lock().upgrade() {
            Some(arc) => return arc,
            None => unreachable!(),
        }
    }
}

to use it

pub struct Test {
    me: MeKeeper<Self>,
    foo:i8,
}

impl Test {
    pub fn new() -> Arc<Self> {
        let arc = Arc::new(Test {
            me: MeKeeper::new(),
            foo:42
        });
        arc.me.save(&arc);
        arc
    }
}

now when an instance of Test wants to call a function that requires it to pass in an Arc it does:

 fn nargle(){
     let me = me.get();
     Ooddle::fertang(me,42);// fertang needs an Arc<T>
 }

the weak use is what the shared_from_this does so as to prevent refcount deadlocks, I stole that idea.

The unreachable path is safe because the only place that can call MeKeeper::get is the instance of T (Test here) that owns it and that call can only happen if the T instance is alive. Hence no none return from weak::upgrade

Upvotes: -1

Alice Ryhl
Alice Ryhl

Reputation: 4239

There is no way to go from a &self to the Arc that self is stored in. This is because:

  1. Rust references have additional assumptions compared to C++ references that would make such a conversion undefined behavior.
  2. Rust's implementation of Arc does not even expose the information necessary to determine whether self is stored in an Arc or not.

Luckily, there is an alternative approach. Instead of creating a &self to the value inside the Arc, and passing that to the method, pass the Arc directly to the method that needs to access it. You can do that like this:

use std::sync::Arc;

struct Shared {
    field: String,
}

impl Shared {
    fn print_field(self: Arc<Self>) {
        let clone: Arc<Shared> = self.clone();
        
        println!("{}", clone.field);
    }
}

Then the print_field function can only be called on an Shared encapsulated in an Arc.

Upvotes: 7

Related Questions