Reputation: 50200
I have an object that I know that is inside an Arc
because all the instances are always Arc
ed. 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
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
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.
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()
}
}
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
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
Reputation: 4239
There is no way to go from a &self
to the Arc
that self
is stored in. This is because:
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