Reputation: 123
I'd like to have a field in struct like this:
struct Foo<T> {
bar: Smart<T>
}
where bar could be either Rc<T
or Weak<T>
, depending on the "ownership relationships" between different instances of Foo. Is there any idiomatic way in Rust how to do this, other than to create a custom enum?
Upvotes: 3
Views: 167
Reputation: 73490
This kind of construct is often called "Either" and there's a crate that looks like it can address some of the usual use-cases: https://docs.rs/either/1.5.3/either/
Then you could write
struct Foo<T> {
bar: Either<Weak<T>, Rc<T>>
}
Then an example function to get an Option<Rc<T>>
might be:
impl <T> Foo<T> {
fn get_rc(self) -> Option<Rc<T>> {
self.bar
.map_left( |weak| weak.upgrade() )
.map_right( |v| Some(v) )
.into_inner()
}
}
Then it can be used like this:
fn main() {
let x = Rc::new(1);
let f_direct = Foo{ bar:Either::Right(x.clone()) };
println!("f_direct.get_rc() = {:?}", f_direct.get_rc());
let f_weak = Foo{ bar:Either::Left(Rc::downgrade(&x)) };
println!("f_weak.get_rc() = {:?}", f_weak.get_rc());
}
Link to complete example in the playground:
Upvotes: 2
Reputation: 16475
An Peter's answer suggested, there is is no such abstraction in the stdlib. You can define a small enum to handle both cases:
use std::rc::{Rc, Weak};
enum MaybeStrong<T> {
Strong(Rc<T>),
Weak(Weak<T>),
}
impl<T> MaybeStrong<T> {
fn get(&self) -> Option<Rc<T>> {
match self {
MaybeStrong::Strong(t) => Some(Rc::clone(t)),
MaybeStrong::Weak(w) => w.upgrade(),
}
}
}
struct Foo<T> {
bar: MaybeStrong<T>
}
impl<T> Foo<T> {
fn from_weak(inner: Weak<T>) -> Self {
Self { bar: MaybeStrong::Weak(inner) }
}
fn from_strong(inner: Rc<T>) -> Self {
Self { bar: MaybeStrong::Strong(inner) }
}
fn say(&self) where T: std::fmt::Debug {
println!("{:?}", self.bar.get())
}
}
fn main() {
let inner = Rc::new("foo!");
Foo::from_weak(Rc::downgrade(&inner)).say();
Foo::from_strong(inner).say();
}
self.bar()
will always return a Some
if it was created from a strong pointer and return None
in case it's a Weak
and it's dangling. Notice that due to the fact that get()
needs to create an owned Rc
first, the method can't return a &T
(including Option<&T>
) because that &T
could be dangling. This also means that all users of bar()
will own one strong count on the inner value while processing, making it safe to use in any case.
Upvotes: 2
Reputation: 58735
Is there any idiomatic way in Rust how to do this, other than to create a custom enum?
Just like most other "either this or that" choices in Rust, an enum
is the idiomatic way.
Upvotes: 2