From e9475b57be66a16e8a7c2b796dc60dff04f86fe9 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sat, 22 Mar 2014 18:12:22 +1100 Subject: [PATCH] std: expand the `Share` docs to make them more precise. And give some examples about exactly what's `Share` and what's not. --- src/libstd/kinds.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/libstd/kinds.rs b/src/libstd/kinds.rs index 2ce41a33c29..f116f61509e 100644 --- a/src/libstd/kinds.rs +++ b/src/libstd/kinds.rs @@ -40,7 +40,51 @@ pub trait Pod { // Empty. } -/// Types that can be safely shared between threads, hence thread-safe. +/// Types that can be safely shared between tasks when aliased. +/// +/// The precise definition is: a type `T` is `Share` if `&T` is +/// thread-safe. In other words, there is no possibility of data races +/// when passing `&T` references between tasks. +/// +/// As one would expect, primitive types like `u8` and `f64` are all +/// `Share`, and so are simple aggregate types containing them (like +/// tuples, structs and enums). More instances of basic `Share` types +/// include "immutable" types like `&T` and those with simple +/// inherited mutability, such as `~T`, `Vec` and most other +/// collection types. (Generic parameters need to be `Share` for their +/// container to be `Share`.) +/// +/// A somewhat surprising consequence of the definition is `&mut T` is +/// `Share` (if `T` is `Share`) even though it seems that it might +/// provide unsynchronised mutation. The trick is a mutable reference +/// stored in an aliasable reference (that is, `& &mut T`) becomes +/// read-only, as if it were a `& &T`, hence there is no risk of a data +/// race. +/// +/// Types that are not `Share` are those that have "interior +/// mutability" in a non-thread-safe way, such as `Cell` and `RefCell` +/// in `std::cell`. These types allow for mutation of their contents +/// even when in an immutable, aliasable slot, e.g. the contents of +/// `&Cell` can be `.set`, and do not ensure data races are +/// impossible, hence they cannot be `Share`. A higher level example +/// of a non-`Share` type is the reference counted pointer +/// `std::rc::Rc`, because any reference `&Rc` can clone a new +/// reference, which modifies the reference counts in a non-atomic +/// way. +/// +/// For cases when one does need thread-safe interior mutability, +/// types like the atomics in `std::sync` and `Mutex` & `RWLock` in +/// the `sync` crate do ensure that any mutation cannot cause data +/// races. Hence these types are `Share`. +/// +/// Users writing their own types with interior mutability (or anything +/// else that is not thread-safe) should use the `NoShare` marker type +/// (from `std::kinds::marker`) to ensure that the compiler doesn't +/// consider the user-defined type to be `Share`. Any types with +/// interior mutability must also use the `std::ty::Unsafe` wrapper +/// around the value(s) which can be mutated when behind a `&` +/// reference; not doing this is undefined behaviour (for example, +/// `transmute`-ing from `&T` to `&mut T` is illegal). #[lang="share"] pub trait Share { // Empty