Skip to content

Instantly share code, notes, and snippets.

@stevedonovan
Created April 14, 2017 13:49
Show Gist options
  • Star 27 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save stevedonovan/7e3a6d8c8921e3eff16c4b11ab82b8d7 to your computer and use it in GitHub Desktop.
Save stevedonovan/7e3a6d8c8921e3eff16c4b11ab82b8d7 to your computer and use it in GitHub Desktop.
An ergonomic way of saying Rc<RefCell>
use std::rc::Rc;
use std::cell::{RefCell,Ref, RefMut};
use std::ops::Deref;
use std::fmt;
#[derive(Clone)]
struct Shared<T> {
v: Rc<RefCell<T>>
}
impl <T> Shared<T> {
fn new(t: T)-> Shared<T> {
Shared{v: Rc::new(RefCell::new(t))}
}
}
impl <T> Shared<T> {
fn borrow(&self) -> Ref<T> {
self.v.borrow()
}
fn borrow_mut(&self) -> RefMut<T> {
self.v.borrow_mut()
}
fn as_ptr(&self) -> *mut T {
self.v.as_ptr()
}
}
impl <T: fmt::Display> fmt::Display for Shared<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.deref())
}
}
impl <T: fmt::Debug> fmt::Debug for Shared<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.deref())
}
}
impl <'a,T> Deref for Shared<T>{
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe {self.as_ptr().as_ref().unwrap()}
}
}
/*
// Cute, but useless, since it needs to be mutable
// and so can't be shared anyway
impl <'a, T> DerefMut for Shared<T>
{ #[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe {self.as_ptr().as_mut().unwrap()}
}
}
*/
fn split (s: Shared<String>) -> Vec<String> {
s.split_whitespace().map(|s| s.to_string()).collect()
}
fn main() {
let s = Shared::new("hello".to_string());
let s2 = s.clone();
s2.borrow_mut().push('!');
println!("{:?}",s2);
// Deref kicking in...
let n = s2.len();
println!("{:?}", n);
// mutation has to be explicit
s2.borrow_mut().push_str(" dolly");
println!("{:?} {}",s2.borrow(), s);
println!("{:?}", split(s2.clone()));
}
@the-shank
Copy link

This is great! And so readable!

@wdanilo
Copy link

wdanilo commented Nov 20, 2019

@stevedonovan I think the implementation is unsound. The unsafe {self.as_ptr().as_ref().unwrap()} part gives you what is inside the RefCell but does not prevent anyone from calling borrow_mut while the deref is still in scope.

@stevedonovan
Copy link
Author

You are completely correct, alas.

@mamcx
Copy link

mamcx commented Dec 15, 2019

Some way to fix it?

@stevedonovan
Copy link
Author

I don't think that Deref can be fixed safely. It basically says that the handed-out reference has same lifetime as shared pointer, which is insufficient. Ref<T> gives you the right guarantee. A pity, really - can still say borrow everywhere (or make it shorter ref :) )

@wdanilo
Copy link

wdanilo commented Dec 28, 2019

It cannot be fixed currently. Deref has associated type Target which cannot be polymorphic (forall t'), because of a few reasons, including lack of HKT. I don't think it will be possible even if GATs land, as forall t' is not what we want to tell there, really. What we want to express is that this 't in Target is the same as t on &'t self. In order to express that, we would either need to change the definition of Deref (not going to happen), or we would need to have some special type checking rules. I do feel that the type system around lifetimes is not flexible as it should be, so hopefully, it could be expressible in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment