auto merge of #10514 : sfackler/rust/mut, r=cmr
This is based off of @blake2-ppc's work on #9429. That PR bitrotted and I haven't been able to contact the original author so I decided to take up the cause. Overview ====== `Mut` encapsulates a mutable, non-nullable slot. The `Cell` type is currently used to do this, but `Cell` is much more commonly used as a workaround for the inability to move values into non-once functions. `Mut` provides a more robust API. `Mut` duplicates the semantics of borrowed pointers with enforcement at runtime instead of compile time. ```rust let x = Mut::new(0); { // make some immutable borrows let p = x.borrow(); let y = *p.get() + 10; // multiple immutable borrows are allowed simultaneously let p2 = x.borrow(); // this would throw a runtime failure // let p_mut = x.borrow_mut(); } // now we can mutably borrow let p = x.borrow_mut(); *p.get() = 10; ``` `borrow` returns a `Ref` type and `borrow_mut` returns a `RefMut` type, both of which are simple smart pointer types with a single method, `get`, which returns a reference to the wrapped data. This also allows `RcMut<T>` to be deleted, as it can be replaced with `Rc<Mut<T>>`. Changes ====== I've done things a little bit differently than the original proposal. * I've added `try_borrow` and `try_borrow_mut` methods that return `Option<Ref<T>>` and `Option<RefMut<T>>` respectively instead of failing on a borrow check failure. I'm not totally sure when that'd be useful, but I don't see any reason to not put them in and @cmr requested them. * `ReadPtr` and `WritePtr` have been renamed to `Ref` and `RefMut` respectively, as `Ref` is to `ref foo` and `RefMut` is to `ref mut foo` as `Mut` is to `mut foo`. * `get` on `MutRef` now takes `&self` instead of `&mut self` for consistency with `&mut`. As @alexcrichton pointed, out this violates soundness by allowing aliasing `&mut` references. * `Cell` is being left as is. It solves a different problem than `Mut` is designed to solve. * There are no longer methods implemented for `Mut<Option<T>>`. Since `Cell` isn't going away, there's less of a need for these, and I didn't feel like they provided a huge benefit, especially as that kind of `impl` is very uncommon in the standard library. Open Questions ============ * `Cell` should now be used exclusively for movement into closures. Should this be enforced by reducing its API to `new` and `take`? It seems like this use case will be completely going away once the transition to `proc` and co. finishes. * Should there be `try_map` and `try_map_mut` methods along with `map` and `map_mut`?
This commit is contained in:
commit
33375a31e8
|
@ -24,7 +24,6 @@ use middle::graph::{Direction, NodeIndex};
|
|||
use util::common::indenter;
|
||||
use util::ppaux::{Repr};
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::hashmap::{HashMap, HashSet};
|
||||
use std::uint;
|
||||
use std::vec;
|
||||
|
@ -106,16 +105,15 @@ pub struct RegionVarBindings {
|
|||
undo_log: ~[UndoLogEntry],
|
||||
|
||||
// This contains the results of inference. It begins as an empty
|
||||
// cell and only acquires a value after inference is complete.
|
||||
// We use a cell vs a mutable option to circumvent borrowck errors.
|
||||
values: Cell<~[VarValue]>,
|
||||
// option and only acquires a value after inference is complete.
|
||||
values: Option<~[VarValue]>,
|
||||
}
|
||||
|
||||
pub fn RegionVarBindings(tcx: ty::ctxt) -> RegionVarBindings {
|
||||
RegionVarBindings {
|
||||
tcx: tcx,
|
||||
var_origins: ~[],
|
||||
values: Cell::new_empty(),
|
||||
values: None,
|
||||
constraints: HashMap::new(),
|
||||
lubs: HashMap::new(),
|
||||
glbs: HashMap::new(),
|
||||
|
@ -226,7 +224,7 @@ impl RegionVarBindings {
|
|||
constraint: Constraint,
|
||||
origin: SubregionOrigin) {
|
||||
// cannot add constraints once regions are resolved
|
||||
assert!(self.values.is_empty());
|
||||
assert!(self.values.is_none());
|
||||
|
||||
debug!("RegionVarBindings: add_constraint({:?})", constraint);
|
||||
|
||||
|
@ -242,7 +240,7 @@ impl RegionVarBindings {
|
|||
sub: Region,
|
||||
sup: Region) {
|
||||
// cannot add constraints once regions are resolved
|
||||
assert!(self.values.is_empty());
|
||||
assert!(self.values.is_none());
|
||||
|
||||
debug!("RegionVarBindings: make_subregion({:?}, {:?})", sub, sup);
|
||||
match (sub, sup) {
|
||||
|
@ -277,7 +275,7 @@ impl RegionVarBindings {
|
|||
b: Region)
|
||||
-> Region {
|
||||
// cannot add constraints once regions are resolved
|
||||
assert!(self.values.is_empty());
|
||||
assert!(self.values.is_none());
|
||||
|
||||
debug!("RegionVarBindings: lub_regions({:?}, {:?})", a, b);
|
||||
match (a, b) {
|
||||
|
@ -300,7 +298,7 @@ impl RegionVarBindings {
|
|||
b: Region)
|
||||
-> Region {
|
||||
// cannot add constraints once regions are resolved
|
||||
assert!(self.values.is_empty());
|
||||
assert!(self.values.is_none());
|
||||
|
||||
debug!("RegionVarBindings: glb_regions({:?}, {:?})", a, b);
|
||||
match (a, b) {
|
||||
|
@ -319,14 +317,14 @@ impl RegionVarBindings {
|
|||
}
|
||||
|
||||
pub fn resolve_var(&mut self, rid: RegionVid) -> ty::Region {
|
||||
if self.values.is_empty() {
|
||||
self.tcx.sess.span_bug(
|
||||
let v = match self.values {
|
||||
None => self.tcx.sess.span_bug(
|
||||
self.var_origins[rid.to_uint()].span(),
|
||||
format!("Attempt to resolve region variable before values have \
|
||||
been computed!"));
|
||||
}
|
||||
been computed!")),
|
||||
Some(ref values) => values[rid.to_uint()]
|
||||
};
|
||||
|
||||
let v = self.values.with_ref(|values| values[rid.to_uint()]);
|
||||
debug!("RegionVarBindings: resolve_var({:?}={})={:?}",
|
||||
rid, rid.to_uint(), v);
|
||||
match v {
|
||||
|
@ -482,7 +480,7 @@ impl RegionVarBindings {
|
|||
debug!("RegionVarBindings: resolve_regions()");
|
||||
let mut errors = opt_vec::Empty;
|
||||
let v = self.infer_variable_values(&mut errors);
|
||||
self.values.put_back(v);
|
||||
self.values = Some(v);
|
||||
errors
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,13 +8,14 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! A mutable, nullable memory location
|
||||
//! Types dealing with dynamic mutability
|
||||
|
||||
#[missing_doc];
|
||||
|
||||
use cast::transmute_mut;
|
||||
use unstable::finally::Finally;
|
||||
use prelude::*;
|
||||
use cast;
|
||||
use util::NonCopyable;
|
||||
|
||||
|
||||
/*
|
||||
A dynamic, mutable location.
|
||||
|
@ -35,14 +36,9 @@ impl<T> Cell<T> {
|
|||
Cell { value: Some(value) }
|
||||
}
|
||||
|
||||
/// Creates a new empty cell with no value inside.
|
||||
pub fn new_empty() -> Cell<T> {
|
||||
Cell { value: None }
|
||||
}
|
||||
|
||||
/// Yields the value, failing if the cell is empty.
|
||||
pub fn take(&self) -> T {
|
||||
let this = unsafe { transmute_mut(self) };
|
||||
let this = unsafe { cast::transmute_mut(self) };
|
||||
if this.is_empty() {
|
||||
fail!("attempt to take an empty cell");
|
||||
}
|
||||
|
@ -52,38 +48,14 @@ impl<T> Cell<T> {
|
|||
|
||||
/// Yields the value if the cell is full, or `None` if it is empty.
|
||||
pub fn take_opt(&self) -> Option<T> {
|
||||
let this = unsafe { transmute_mut(self) };
|
||||
let this = unsafe { cast::transmute_mut(self) };
|
||||
this.value.take()
|
||||
}
|
||||
|
||||
/// Returns the value, failing if the cell is full.
|
||||
pub fn put_back(&self, value: T) {
|
||||
let this = unsafe { transmute_mut(self) };
|
||||
if !this.is_empty() {
|
||||
fail!("attempt to put a value back into a full cell");
|
||||
}
|
||||
this.value = Some(value);
|
||||
}
|
||||
|
||||
/// Returns true if the cell is empty and false if the cell is full.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.value.is_none()
|
||||
}
|
||||
|
||||
/// Calls a closure with a reference to the value.
|
||||
pub fn with_ref<R>(&self, op: |v: &T| -> R) -> R {
|
||||
do self.with_mut_ref |ptr| { op(ptr) }
|
||||
}
|
||||
|
||||
/// Calls a closure with a mutable reference to the value.
|
||||
pub fn with_mut_ref<R>(&self, op: |v: &mut T| -> R) -> R {
|
||||
let mut v = Some(self.take());
|
||||
do (|| {
|
||||
op(v.get_mut_ref())
|
||||
}).finally {
|
||||
self.put_back(v.take_unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -93,38 +65,304 @@ fn test_basic() {
|
|||
let value = value_cell.take();
|
||||
assert!(value == ~10);
|
||||
assert!(value_cell.is_empty());
|
||||
value_cell.put_back(value);
|
||||
assert!(!value_cell.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn test_take_empty() {
|
||||
let value_cell: Cell<~int> = Cell::new_empty();
|
||||
let value_cell: Cell<~int> = Cell::new(~0);
|
||||
value_cell.take();
|
||||
value_cell.take();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn test_put_back_non_empty() {
|
||||
let value_cell = Cell::new(~10);
|
||||
value_cell.put_back(~20);
|
||||
|
||||
/// A mutable memory location with dynamically checked borrow rules
|
||||
#[no_freeze]
|
||||
pub struct RefCell<T> {
|
||||
priv value: T,
|
||||
priv borrow: BorrowFlag,
|
||||
priv nc: NonCopyable
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_ref() {
|
||||
let good = 6;
|
||||
let c = Cell::new(~[1, 2, 3, 4, 5, 6]);
|
||||
let l = do c.with_ref() |v| { v.len() };
|
||||
assert_eq!(l, good);
|
||||
// Values [1, MAX-1] represent the number of `Ref` active
|
||||
// (will not outgrow its range since `uint` is the size of the address space)
|
||||
type BorrowFlag = uint;
|
||||
static UNUSED: BorrowFlag = 0;
|
||||
static WRITING: BorrowFlag = -1;
|
||||
|
||||
impl<T> RefCell<T> {
|
||||
/// Create a new `RefCell` containing `value`
|
||||
pub fn new(value: T) -> RefCell<T> {
|
||||
RefCell {
|
||||
value: value,
|
||||
borrow: UNUSED,
|
||||
nc: NonCopyable
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes the `RefCell`, returning the wrapped value.
|
||||
pub fn unwrap(self) -> T {
|
||||
assert!(self.borrow == UNUSED);
|
||||
self.value
|
||||
}
|
||||
|
||||
unsafe fn as_mut<'a>(&'a self) -> &'a mut RefCell<T> {
|
||||
cast::transmute_mut(self)
|
||||
}
|
||||
|
||||
/// Attempts to immutably borrow the wrapped value.
|
||||
///
|
||||
/// The borrow lasts until the returned `Ref` exits scope. Multiple
|
||||
/// immutable borrows can be taken out at the same time.
|
||||
///
|
||||
/// Returns `None` if the value is currently mutably borrowed.
|
||||
pub fn try_borrow<'a>(&'a self) -> Option<Ref<'a, T>> {
|
||||
match self.borrow {
|
||||
WRITING => None,
|
||||
_ => {
|
||||
unsafe { self.as_mut().borrow += 1; }
|
||||
Some(Ref { parent: self })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Immutably borrows the wrapped value.
|
||||
///
|
||||
/// The borrow lasts until the returned `Ref` exits scope. Multiple
|
||||
/// immutable borrows can be taken out at the same time.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Fails if the value is currently mutably borrowed.
|
||||
pub fn borrow<'a>(&'a self) -> Ref<'a, T> {
|
||||
match self.try_borrow() {
|
||||
Some(ptr) => ptr,
|
||||
None => fail!("RefCell<T> already mutably borrowed")
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutably borrows the wrapped value.
|
||||
///
|
||||
/// The borrow lasts untile the returned `RefMut` exits scope. The value
|
||||
/// cannot be borrowed while this borrow is active.
|
||||
///
|
||||
/// Returns `None` if the value is currently borrowed.
|
||||
pub fn try_borrow_mut<'a>(&'a self) -> Option<RefMut<'a, T>> {
|
||||
match self.borrow {
|
||||
UNUSED => unsafe {
|
||||
let mut_self = self.as_mut();
|
||||
mut_self.borrow = WRITING;
|
||||
Some(RefMut { parent: mut_self })
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutably borrows the wrapped value.
|
||||
///
|
||||
/// The borrow lasts untile the returned `RefMut` exits scope. The value
|
||||
/// cannot be borrowed while this borrow is active.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Fails if the value is currently borrowed.
|
||||
pub fn borrow_mut<'a>(&'a self) -> RefMut<'a, T> {
|
||||
match self.try_borrow_mut() {
|
||||
Some(ptr) => ptr,
|
||||
None => fail!("RefCell<T> already borrowed")
|
||||
}
|
||||
}
|
||||
|
||||
/// Immutably borrows the wrapped value and applies `blk` to it.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Fails if the value is currently mutably borrowed.
|
||||
#[inline]
|
||||
pub fn with<U>(&self, blk: |&T| -> U) -> U {
|
||||
let ptr = self.borrow();
|
||||
blk(ptr.get())
|
||||
}
|
||||
|
||||
/// Mutably borrows the wrapped value and applies `blk` to it.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Fails if the value is currently borrowed.
|
||||
#[inline]
|
||||
pub fn with_mut<U>(&self, blk: |&mut T| -> U) -> U {
|
||||
let mut ptr = self.borrow_mut();
|
||||
blk(ptr.get())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_mut_ref() {
|
||||
let good = ~[1, 2, 3];
|
||||
let v = ~[1, 2];
|
||||
let c = Cell::new(v);
|
||||
do c.with_mut_ref() |v| { v.push(3); }
|
||||
let v = c.take();
|
||||
assert_eq!(v, good);
|
||||
impl<T: Clone> Clone for RefCell<T> {
|
||||
fn clone(&self) -> RefCell<T> {
|
||||
let x = self.borrow();
|
||||
RefCell::new(x.get().clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: DeepClone> DeepClone for RefCell<T> {
|
||||
fn deep_clone(&self) -> RefCell<T> {
|
||||
let x = self.borrow();
|
||||
RefCell::new(x.get().deep_clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq> Eq for RefCell<T> {
|
||||
fn eq(&self, other: &RefCell<T>) -> bool {
|
||||
let a = self.borrow();
|
||||
let b = other.borrow();
|
||||
a.get() == b.get()
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps a borrowed reference to a value in a `RefCell` box.
|
||||
pub struct Ref<'box, T> {
|
||||
priv parent: &'box RefCell<T>
|
||||
}
|
||||
|
||||
#[unsafe_destructor]
|
||||
impl<'box, T> Drop for Ref<'box, T> {
|
||||
fn drop(&mut self) {
|
||||
assert!(self.parent.borrow != WRITING && self.parent.borrow != UNUSED);
|
||||
unsafe { self.parent.as_mut().borrow -= 1; }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'box, T> Ref<'box, T> {
|
||||
/// Retrieve an immutable reference to the stored value.
|
||||
#[inline]
|
||||
pub fn get<'a>(&'a self) -> &'a T {
|
||||
&self.parent.value
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps a mutable borrowed reference to a value in a `RefCell` box.
|
||||
pub struct RefMut<'box, T> {
|
||||
priv parent: &'box mut RefCell<T>
|
||||
}
|
||||
|
||||
#[unsafe_destructor]
|
||||
impl<'box, T> Drop for RefMut<'box, T> {
|
||||
fn drop(&mut self) {
|
||||
assert!(self.parent.borrow == WRITING);
|
||||
self.parent.borrow = UNUSED;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'box, T> RefMut<'box, T> {
|
||||
/// Retrieve a mutable reference to the stored value.
|
||||
#[inline]
|
||||
pub fn get<'a>(&'a mut self) -> &'a mut T {
|
||||
&mut self.parent.value
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn double_imm_borrow() {
|
||||
let x = RefCell::new(0);
|
||||
let _b1 = x.borrow();
|
||||
x.borrow();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_mut_then_imm_borrow() {
|
||||
let x = RefCell::new(0);
|
||||
let _b1 = x.borrow_mut();
|
||||
assert!(x.try_borrow().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_imm_then_borrow_mut() {
|
||||
let x = RefCell::new(0);
|
||||
let _b1 = x.borrow();
|
||||
assert!(x.try_borrow_mut().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_double_borrow_mut() {
|
||||
let x = RefCell::new(0);
|
||||
let _b1 = x.borrow_mut();
|
||||
assert!(x.try_borrow_mut().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn imm_release_borrow_mut() {
|
||||
let x = RefCell::new(0);
|
||||
{
|
||||
let _b1 = x.borrow();
|
||||
}
|
||||
x.borrow_mut();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mut_release_borrow_mut() {
|
||||
let x = RefCell::new(0);
|
||||
{
|
||||
let _b1 = x.borrow_mut();
|
||||
}
|
||||
x.borrow();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_borrow_single_release_no_borrow_mut() {
|
||||
let x = RefCell::new(0);
|
||||
let _b1 = x.borrow();
|
||||
{
|
||||
let _b2 = x.borrow();
|
||||
}
|
||||
assert!(x.try_borrow_mut().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_ok() {
|
||||
let x = RefCell::new(0);
|
||||
assert_eq!(1, x.with(|x| *x+1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn mut_borrow_with() {
|
||||
let x = RefCell::new(0);
|
||||
let _b1 = x.borrow_mut();
|
||||
x.with(|x| *x+1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn borrow_with() {
|
||||
let x = RefCell::new(0);
|
||||
let _b1 = x.borrow();
|
||||
assert_eq!(1, x.with(|x| *x+1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_mut_ok() {
|
||||
let x = RefCell::new(0);
|
||||
x.with_mut(|x| *x += 1);
|
||||
let b = x.borrow();
|
||||
assert_eq!(1, *b.get());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn borrow_with_mut() {
|
||||
let x = RefCell::new(0);
|
||||
let _b = x.borrow();
|
||||
x.with_mut(|x| *x += 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn discard_doesnt_unborrow() {
|
||||
let x = RefCell::new(0);
|
||||
let _b = x.borrow();
|
||||
let _ = _b;
|
||||
let _b = x.borrow_mut();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,26 +55,26 @@ impl<T: DeepClone + Send + 'static> DeepClone for Gc<T> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use cell::Cell;
|
||||
use cell::RefCell;
|
||||
|
||||
#[test]
|
||||
fn test_clone() {
|
||||
let x = Gc::new(Cell::new(5));
|
||||
let x = Gc::new(RefCell::new(5));
|
||||
let y = x.clone();
|
||||
do x.borrow().with_mut_ref |inner| {
|
||||
do x.borrow().with_mut |inner| {
|
||||
*inner = 20;
|
||||
}
|
||||
assert_eq!(y.borrow().take(), 20);
|
||||
assert_eq!(y.borrow().with(|x| *x), 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deep_clone() {
|
||||
let x = Gc::new(Cell::new(5));
|
||||
let x = Gc::new(RefCell::new(5));
|
||||
let y = x.deep_clone();
|
||||
do x.borrow().with_mut_ref |inner| {
|
||||
do x.borrow().with_mut |inner| {
|
||||
*inner = 20;
|
||||
}
|
||||
assert_eq!(y.borrow().take(), 5);
|
||||
assert_eq!(y.borrow().with(|x| *x), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
262
src/libstd/rc.rs
262
src/libstd/rc.rs
|
@ -14,10 +14,6 @@ The `Rc` type provides shared ownership of an immutable value. Destruction is de
|
|||
will occur as soon as the last owner is gone. It is marked as non-sendable because it avoids the
|
||||
overhead of atomic reference counting.
|
||||
|
||||
The `RcMut` type provides shared ownership of a mutable value. Since multiple owners prevent
|
||||
inherited mutability, a dynamic freezing check is used to maintain the invariant that an `&mut`
|
||||
reference is a unique handle and the type is marked as non-`Freeze`.
|
||||
|
||||
*/
|
||||
|
||||
use ptr::RawPtr;
|
||||
|
@ -25,6 +21,7 @@ use unstable::intrinsics::transmute;
|
|||
use ops::Drop;
|
||||
use kinds::{Freeze, Send};
|
||||
use clone::{Clone, DeepClone};
|
||||
use cell::RefCell;
|
||||
|
||||
struct RcBox<T> {
|
||||
value: T,
|
||||
|
@ -58,6 +55,16 @@ impl<T: Send> Rc<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Freeze> Rc<RefCell<T>> {
|
||||
/// Construct a new reference-counted box from a `RefCell`-wrapped `Freeze` value
|
||||
#[inline]
|
||||
pub fn from_mut(value: RefCell<T>) -> Rc<RefCell<T>> {
|
||||
unsafe {
|
||||
Rc::new_unchecked(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Rc<T> {
|
||||
/// Unsafety construct a new reference-counted box from any value.
|
||||
///
|
||||
|
@ -109,26 +116,26 @@ impl<T> Drop for Rc<T> {
|
|||
#[cfg(test)]
|
||||
mod test_rc {
|
||||
use super::*;
|
||||
use cell::Cell;
|
||||
use cell::RefCell;
|
||||
|
||||
#[test]
|
||||
fn test_clone() {
|
||||
let x = Rc::from_send(Cell::new(5));
|
||||
let x = Rc::from_send(RefCell::new(5));
|
||||
let y = x.clone();
|
||||
do x.borrow().with_mut_ref |inner| {
|
||||
do x.borrow().with_mut |inner| {
|
||||
*inner = 20;
|
||||
}
|
||||
assert_eq!(y.borrow().take(), 20);
|
||||
assert_eq!(y.borrow().with(|v| *v), 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deep_clone() {
|
||||
let x = Rc::from_send(Cell::new(5));
|
||||
let x = Rc::from_send(RefCell::new(5));
|
||||
let y = x.deep_clone();
|
||||
do x.borrow().with_mut_ref |inner| {
|
||||
do x.borrow().with_mut |inner| {
|
||||
*inner = 20;
|
||||
}
|
||||
assert_eq!(y.borrow().take(), 5);
|
||||
assert_eq!(y.borrow().with(|v| *v), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -150,237 +157,10 @@ mod test_rc {
|
|||
let x = Rc::from_send(~5);
|
||||
assert_eq!(**x.borrow(), 5);
|
||||
}
|
||||
}
|
||||
|
||||
#[deriving(Eq)]
|
||||
enum Borrow {
|
||||
Mutable,
|
||||
Immutable,
|
||||
Nothing
|
||||
}
|
||||
|
||||
struct RcMutBox<T> {
|
||||
value: T,
|
||||
count: uint,
|
||||
borrow: Borrow
|
||||
}
|
||||
|
||||
/// Mutable reference counted pointer type
|
||||
#[no_send]
|
||||
#[no_freeze]
|
||||
#[unsafe_no_drop_flag]
|
||||
pub struct RcMut<T> {
|
||||
priv ptr: *mut RcMutBox<T>,
|
||||
}
|
||||
|
||||
impl<T: Freeze> RcMut<T> {
|
||||
/// Construct a new mutable reference-counted box from a `Freeze` value
|
||||
#[inline]
|
||||
pub fn new(value: T) -> RcMut<T> {
|
||||
unsafe { RcMut::new_unchecked(value) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send> RcMut<T> {
|
||||
/// Construct a new mutable reference-counted box from a `Send` value
|
||||
#[inline]
|
||||
pub fn from_send(value: T) -> RcMut<T> {
|
||||
unsafe { RcMut::new_unchecked(value) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RcMut<T> {
|
||||
/// Unsafety construct a new mutable reference-counted box from any value.
|
||||
///
|
||||
/// It is possible to create cycles, which will leak, and may interact
|
||||
/// poorly with managed pointers.
|
||||
#[inline]
|
||||
pub unsafe fn new_unchecked(value: T) -> RcMut<T> {
|
||||
RcMut{ptr: transmute(~RcMutBox{value: value, count: 1, borrow: Nothing})}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RcMut<T> {
|
||||
/// Fails if there is already a mutable borrow of the box
|
||||
#[inline]
|
||||
pub fn with_borrow<U>(&self, f: |&T| -> U) -> U {
|
||||
unsafe {
|
||||
assert!((*self.ptr).borrow != Mutable);
|
||||
let previous = (*self.ptr).borrow;
|
||||
(*self.ptr).borrow = Immutable;
|
||||
let res = f(&(*self.ptr).value);
|
||||
(*self.ptr).borrow = previous;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// Fails if there is already a mutable or immutable borrow of the box
|
||||
#[inline]
|
||||
pub fn with_mut_borrow<U>(&self, f: |&mut T| -> U) -> U {
|
||||
unsafe {
|
||||
assert_eq!((*self.ptr).borrow, Nothing);
|
||||
(*self.ptr).borrow = Mutable;
|
||||
let res = f(&mut (*self.ptr).value);
|
||||
(*self.ptr).borrow = Nothing;
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_destructor]
|
||||
impl<T> Drop for RcMut<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if self.ptr.is_not_null() {
|
||||
(*self.ptr).count -= 1;
|
||||
if (*self.ptr).count == 0 {
|
||||
let _: ~RcMutBox<T> = transmute(self.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for RcMut<T> {
|
||||
/// Return a shallow copy of the reference counted pointer.
|
||||
#[inline]
|
||||
fn clone(&self) -> RcMut<T> {
|
||||
unsafe {
|
||||
(*self.ptr).count += 1;
|
||||
RcMut{ptr: self.ptr}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: DeepClone> DeepClone for RcMut<T> {
|
||||
/// Return a deep copy of the reference counted pointer.
|
||||
#[inline]
|
||||
fn deep_clone(&self) -> RcMut<T> {
|
||||
do self.with_borrow |x| {
|
||||
// FIXME: #6497: should avoid freeze (slow)
|
||||
unsafe { RcMut::new_unchecked(x.deep_clone()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_rc_mut {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_clone() {
|
||||
let x = RcMut::from_send(5);
|
||||
let y = x.clone();
|
||||
do x.with_mut_borrow |value| {
|
||||
*value = 20;
|
||||
}
|
||||
do y.with_borrow |value| {
|
||||
assert_eq!(*value, 20);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deep_clone() {
|
||||
let x = RcMut::new(5);
|
||||
let y = x.deep_clone();
|
||||
do x.with_mut_borrow |value| {
|
||||
*value = 20;
|
||||
}
|
||||
do y.with_borrow |value| {
|
||||
assert_eq!(*value, 5);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn borrow_many() {
|
||||
let x = RcMut::from_send(5);
|
||||
let y = x.clone();
|
||||
|
||||
do x.with_borrow |a| {
|
||||
assert_eq!(*a, 5);
|
||||
do y.with_borrow |b| {
|
||||
assert_eq!(*b, 5);
|
||||
do x.with_borrow |c| {
|
||||
assert_eq!(*c, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modify() {
|
||||
let x = RcMut::new(5);
|
||||
let y = x.clone();
|
||||
|
||||
do y.with_mut_borrow |a| {
|
||||
assert_eq!(*a, 5);
|
||||
*a = 6;
|
||||
}
|
||||
|
||||
do x.with_borrow |a| {
|
||||
assert_eq!(*a, 6);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn release_immutable() {
|
||||
let x = RcMut::from_send(5);
|
||||
do x.with_borrow |_| {}
|
||||
do x.with_mut_borrow |_| {}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn release_mutable() {
|
||||
let x = RcMut::new(5);
|
||||
do x.with_mut_borrow |_| {}
|
||||
do x.with_borrow |_| {}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn frozen() {
|
||||
let x = RcMut::from_send(5);
|
||||
let y = x.clone();
|
||||
|
||||
do x.with_borrow |_| {
|
||||
do y.with_mut_borrow |_| {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn mutable_dupe() {
|
||||
let x = RcMut::new(5);
|
||||
let y = x.clone();
|
||||
|
||||
do x.with_mut_borrow |_| {
|
||||
do y.with_mut_borrow |_| {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn mutable_freeze() {
|
||||
let x = RcMut::from_send(5);
|
||||
let y = x.clone();
|
||||
|
||||
do x.with_mut_borrow |_| {
|
||||
do y.with_borrow |_| {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn restore_freeze() {
|
||||
let x = RcMut::new(5);
|
||||
let y = x.clone();
|
||||
|
||||
do x.with_borrow |_| {
|
||||
do x.with_borrow |_| {}
|
||||
do y.with_mut_borrow |_| {}
|
||||
}
|
||||
fn test_from_mut() {
|
||||
let a = 10;
|
||||
let _x = Rc::from_mut(RefCell::new(&a));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,9 +22,10 @@ use rt::select::{SelectInner, SelectPortInner};
|
|||
use select::{Select, SelectPort};
|
||||
use unstable::atomics::{AtomicUint, AtomicOption, Acquire, Relaxed, SeqCst};
|
||||
use unstable::sync::UnsafeArc;
|
||||
use util;
|
||||
use util::Void;
|
||||
use comm::{GenericChan, GenericSmartChan, GenericPort, Peekable, SendDeferred};
|
||||
use cell::Cell;
|
||||
use cell::{Cell, RefCell};
|
||||
use clone::Clone;
|
||||
use tuple::ImmutableTuple;
|
||||
|
||||
|
@ -431,28 +432,28 @@ type StreamPortOne<T> = PortOne<StreamPayload<T>>;
|
|||
|
||||
/// A channel with unbounded size.
|
||||
pub struct Chan<T> {
|
||||
// FIXME #5372. Using Cell because we don't take &mut self
|
||||
next: Cell<StreamChanOne<T>>
|
||||
// FIXME #5372. Using RefCell because we don't take &mut self
|
||||
next: RefCell<StreamChanOne<T>>
|
||||
}
|
||||
|
||||
/// An port with unbounded size.
|
||||
pub struct Port<T> {
|
||||
// FIXME #5372. Using Cell because we don't take &mut self
|
||||
next: Cell<StreamPortOne<T>>
|
||||
// FIXME #5372. Using RefCell because we don't take &mut self
|
||||
next: RefCell<Option<StreamPortOne<T>>>
|
||||
}
|
||||
|
||||
pub fn stream<T: Send>() -> (Port<T>, Chan<T>) {
|
||||
let (pone, cone) = oneshot();
|
||||
let port = Port { next: Cell::new(pone) };
|
||||
let chan = Chan { next: Cell::new(cone) };
|
||||
let port = Port { next: RefCell::new(Some(pone)) };
|
||||
let chan = Chan { next: RefCell::new(cone) };
|
||||
return (port, chan);
|
||||
}
|
||||
|
||||
impl<T: Send> Chan<T> {
|
||||
fn try_send_inner(&self, val: T, do_resched: bool) -> bool {
|
||||
let (next_pone, next_cone) = oneshot();
|
||||
let cone = self.next.take();
|
||||
self.next.put_back(next_cone);
|
||||
let (next_pone, mut cone) = oneshot();
|
||||
let mut b = self.next.borrow_mut();
|
||||
util::swap(&mut cone, b.get());
|
||||
cone.try_send_inner(StreamPayload { val: val, next: next_pone }, do_resched)
|
||||
}
|
||||
}
|
||||
|
@ -489,10 +490,11 @@ impl<T: Send> GenericPort<T> for Port<T> {
|
|||
}
|
||||
|
||||
fn try_recv(&self) -> Option<T> {
|
||||
do self.next.take_opt().map_default(None) |pone| {
|
||||
let mut b = self.next.borrow_mut();
|
||||
do b.get().take().map_default(None) |pone| {
|
||||
match pone.try_recv() {
|
||||
Some(StreamPayload { val, next }) => {
|
||||
self.next.put_back(next);
|
||||
*b.get() = Some(next);
|
||||
Some(val)
|
||||
}
|
||||
None => None
|
||||
|
@ -503,7 +505,7 @@ impl<T: Send> GenericPort<T> for Port<T> {
|
|||
|
||||
impl<T: Send> Peekable<T> for Port<T> {
|
||||
fn peek(&self) -> bool {
|
||||
self.next.with_mut_ref(|p| p.peek())
|
||||
self.next.with_mut(|p| p.get_mut_ref().peek())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -514,18 +516,18 @@ impl<T: Send> Peekable<T> for Port<T> {
|
|||
impl<'self, T: Send> SelectInner for &'self Port<T> {
|
||||
#[inline]
|
||||
fn optimistic_check(&mut self) -> bool {
|
||||
do self.next.with_mut_ref |pone| { pone.optimistic_check() }
|
||||
do self.next.with_mut |pone| { pone.get_mut_ref().optimistic_check() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn block_on(&mut self, sched: &mut Scheduler, task: BlockedTask) -> bool {
|
||||
let task = Cell::new(task);
|
||||
do self.next.with_mut_ref |pone| { pone.block_on(sched, task.take()) }
|
||||
let mut b = self.next.borrow_mut();
|
||||
b.get().get_mut_ref().block_on(sched, task)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unblock_from(&mut self) -> bool {
|
||||
do self.next.with_mut_ref |pone| { pone.unblock_from() }
|
||||
do self.next.with_mut |pone| { pone.get_mut_ref().unblock_from() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -552,9 +554,10 @@ impl<T: Send> Select for Port<T> { }
|
|||
|
||||
impl<'self, T: Send> SelectPortInner<T> for &'self Port<T> {
|
||||
fn recv_ready(self) -> Option<T> {
|
||||
match self.next.take().recv_ready() {
|
||||
let mut b = self.next.borrow_mut();
|
||||
match b.get().take_unwrap().recv_ready() {
|
||||
Some(StreamPayload { val, next }) => {
|
||||
self.next.put_back(next);
|
||||
*b.get() = Some(next);
|
||||
Some(val)
|
||||
}
|
||||
None => None
|
||||
|
@ -571,7 +574,7 @@ pub struct SharedChan<T> {
|
|||
|
||||
impl<T: Send> SharedChan<T> {
|
||||
pub fn new(chan: Chan<T>) -> SharedChan<T> {
|
||||
let next = chan.next.take();
|
||||
let next = chan.next.unwrap();
|
||||
let next = AtomicOption::new(~next);
|
||||
SharedChan { next: UnsafeArc::new(next) }
|
||||
}
|
||||
|
@ -625,7 +628,7 @@ pub struct SharedPort<T> {
|
|||
impl<T: Send> SharedPort<T> {
|
||||
pub fn new(port: Port<T>) -> SharedPort<T> {
|
||||
// Put the data port into a new link pipe
|
||||
let next_data_port = port.next.take();
|
||||
let next_data_port = port.next.unwrap().unwrap();
|
||||
let (next_link_port, next_link_chan) = oneshot();
|
||||
next_link_chan.send(next_data_port);
|
||||
let next_link = AtomicOption::new(~next_link_port);
|
||||
|
|
|
@ -81,8 +81,7 @@ fn main() {
|
|||
let num_tasks = from_str::<uint>(args[1]).unwrap();
|
||||
let msg_per_task = from_str::<uint>(args[2]).unwrap();
|
||||
|
||||
let (num_chan, num_port) = init();
|
||||
let num_chan = Cell::new(num_chan);
|
||||
let (mut num_chan, num_port) = init();
|
||||
|
||||
let start = time::precise_time_s();
|
||||
|
||||
|
@ -92,7 +91,7 @@ fn main() {
|
|||
for i in range(1u, num_tasks) {
|
||||
//error!("spawning %?", i);
|
||||
let (new_chan, num_port) = init();
|
||||
let num_chan2 = Cell::new(num_chan.take());
|
||||
let num_chan2 = Cell::new(num_chan);
|
||||
let num_port = Cell::new(num_port);
|
||||
let new_future = do Future::spawn() {
|
||||
let num_chan = num_chan2.take();
|
||||
|
@ -100,11 +99,11 @@ fn main() {
|
|||
thread_ring(i, msg_per_task, num_chan, num_port1)
|
||||
};
|
||||
futures.push(new_future);
|
||||
num_chan.put_back(new_chan);
|
||||
num_chan = new_chan;
|
||||
};
|
||||
|
||||
// do our iteration
|
||||
thread_ring(0, msg_per_task, num_chan.take(), num_port);
|
||||
thread_ring(0, msg_per_task, num_chan, num_port);
|
||||
|
||||
// synchronize
|
||||
for f in futures.mut_iter() {
|
||||
|
|
|
@ -77,8 +77,7 @@ fn main() {
|
|||
let num_tasks = from_str::<uint>(args[1]).unwrap();
|
||||
let msg_per_task = from_str::<uint>(args[2]).unwrap();
|
||||
|
||||
let (num_chan, num_port) = init();
|
||||
let num_chan = Cell::new(num_chan);
|
||||
let (mut num_chan, num_port) = init();
|
||||
|
||||
let start = time::precise_time_s();
|
||||
|
||||
|
@ -88,7 +87,7 @@ fn main() {
|
|||
for i in range(1u, num_tasks) {
|
||||
//error!("spawning %?", i);
|
||||
let (new_chan, num_port) = init();
|
||||
let num_chan2 = Cell::new(num_chan.take());
|
||||
let num_chan2 = Cell::new(num_chan);
|
||||
let num_port = Cell::new(num_port);
|
||||
let new_future = do Future::spawn {
|
||||
let num_chan = num_chan2.take();
|
||||
|
@ -96,11 +95,11 @@ fn main() {
|
|||
thread_ring(i, msg_per_task, num_chan, num_port1)
|
||||
};
|
||||
futures.push(new_future);
|
||||
num_chan.put_back(new_chan);
|
||||
num_chan = new_chan;
|
||||
};
|
||||
|
||||
// do our iteration
|
||||
thread_ring(0, msg_per_task, num_chan.take(), num_port);
|
||||
thread_ring(0, msg_per_task, num_chan, num_port);
|
||||
|
||||
// synchronize
|
||||
for f in futures.mut_iter() {
|
||||
|
|
|
@ -8,21 +8,22 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::rc::RcMut;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
trait Foo
|
||||
{
|
||||
fn set(&mut self, v: RcMut<A>);
|
||||
fn set(&mut self, v: Rc<RefCell<A>>);
|
||||
}
|
||||
|
||||
struct B
|
||||
{
|
||||
v: Option<RcMut<A>>
|
||||
v: Option<Rc<RefCell<A>>>
|
||||
}
|
||||
|
||||
impl Foo for B
|
||||
{
|
||||
fn set(&mut self, v: RcMut<A>)
|
||||
fn set(&mut self, v: Rc<RefCell<A>>)
|
||||
{
|
||||
self.v = Some(v);
|
||||
}
|
||||
|
@ -36,7 +37,9 @@ struct A
|
|||
fn main()
|
||||
{
|
||||
let a = A {v: ~B{v: None} as ~Foo}; //~ ERROR cannot pack type `~B`, which does not fulfill `Send`
|
||||
let v = RcMut::new(a); //~ ERROR instantiating a type parameter with an incompatible type
|
||||
let v = Rc::from_send(RefCell::new(a));
|
||||
let w = v.clone();
|
||||
v.with_mut_borrow(|p| {p.v.set(w.clone());})
|
||||
let b = v.borrow();
|
||||
let mut b = b.borrow_mut();
|
||||
b.get().v.set(w.clone());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
fn main() {
|
||||
let m = RefCell::new(0);
|
||||
let mut b = m.borrow_mut();
|
||||
let b1 = b.get();
|
||||
let b2 = b.get(); //~ ERROR cannot borrow `b` as mutable more than once at a time
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
fn f<T: Freeze>(_: T) {}
|
||||
|
||||
fn main() {
|
||||
let x = RefCell::new(0);
|
||||
f(x); //~ ERROR: which does not fulfill `Freeze`
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
fn main() {
|
||||
let m = RefCell::new(0);
|
||||
let p;
|
||||
{
|
||||
let b = m.borrow();
|
||||
p = b.get(); //~ ERROR borrowed value does not live long enough
|
||||
}
|
||||
}
|
|
@ -8,13 +8,14 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::rc::RcMut;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
fn o<T: Send>(_: &T) {}
|
||||
fn c<T: Freeze>(_: &T) {}
|
||||
|
||||
fn main() {
|
||||
let x = RcMut::from_send(0);
|
||||
o(&x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::RcMut<int>`, which does not fulfill `Send`
|
||||
c(&x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::RcMut<int>`, which does not fulfill `Freeze`
|
||||
let x = Rc::from_send(RefCell::new(0));
|
||||
o(&x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::Rc<std::cell::RefCell<int>>`, which does not fulfill `Send`
|
||||
c(&x); //~ ERROR instantiating a type parameter with an incompatible type `std::rc::Rc<std::cell::RefCell<int>>`, which does not fulfill `Freeze`
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue