Skip to content

Commit

Permalink
Issue #44: make reference semantics more obvious - not a copy of the …
Browse files Browse the repository at this point in the history
…data
  • Loading branch information
stevedonovan committed Jun 11, 2017
1 parent b1d0aba commit a32bedf
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 11 deletions.
15 changes: 12 additions & 3 deletions code/thread5.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
// thread5.rs
// thread5.rs
use std::thread;
use std::sync::Arc;

struct MyString(String);

impl MyString {
fn new(s: &str) -> MyString {
MyString(s.to_string())
}
}

fn main() {
let mut threads = Vec::new();
let name = Arc::new("dolly".to_string());
let name = Arc::new(MyString::new("dolly"));

for i in 0..5 {
let tname = name.clone();
let t = thread::spawn(move || {
println!("hello {} count {}",tname,i);
println!("hello {} count {}", tname.0, i);
});
threads.push(t);
}

for t in threads {
t.join().expect("thread failed");
}
Expand Down
29 changes: 21 additions & 8 deletions src/7-shared-and-networking.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,25 +290,35 @@ Threads can't share the same environment - by _design_ in Rust. In particular,
they cannot share regular references because the closures move their captured variables.

_shared references_ are fine however - but you cannot use `Rc` for this. This is because
`Rc` is not _thread safe_ - it's optimized to be fast for the non-threaded case. For
threads, you need `std::sync::Arc` - 'Arc' stands for 'Atomic Reference Counting'. That
is, it guarantees that the reference count will be modified in one logical operation. To
make this guarantee, it must ensure that the operation is locked so that only the current
thread has access.
`Rc` is not _thread safe_ - it's optimized to be fast for the non-threaded case.
Fortunately it is a compile error to use `Rc` here!

For threads, you need `std::sync::Arc` - 'Arc' stands for 'Atomic Reference Counting'.
That is, it guarantees that the reference count will be modified in one logical operation.
To make this guarantee, it must ensure that the operation is locked so that only the current
thread has access. `clone` is still much cheaper than actually making a copy however.

```rust
// thread5.rs
use std::thread;
use std::sync::Arc;

struct MyString(String);

impl MyString {
fn new(s: &str) -> MyString {
MyString(s.to_string())
}
}

fn main() {
let mut threads = Vec::new();
let name = Arc::new("dolly".to_string());
let name = Arc::new(MyString::new("dolly"));

for i in 0..5 {
let tname = name.clone();
let t = thread::spawn(move || {
println!("hello {} count {}", tname,i);
println!("hello {} count {}", tname.0, i);
});
threads.push(t);
}
Expand All @@ -319,7 +329,10 @@ fn main() {
}
```

So the shared reference `name` is passed to each new thread by making a new reference
I"ve deliberately created a wrapper type for `String` here (a 'newtype') since
our `MyString` does not implement `Clone`. But the shared reference can be cloned!

The shared reference `name` is passed to each new thread by making a new reference
with `clone` and moving it into the closure. It's a little verbose, but this is a safe
pattern. Safety is important in concurrency precisely because the problems are so
unpredictable. A program may run fine on your machine, but occasionally crash on the
Expand Down

0 comments on commit a32bedf

Please sign in to comment.