about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlisdair Owens <awo101@zepler.net>2015-07-23 10:44:04 +0100
committerAlisdair Owens <awo101@zepler.net>2015-07-23 15:07:26 +0100
commit38c5af86a1412a29a367c856d83cd08dd7e92a33 (patch)
treec4f4a699c57002b2beb16de5eb7add799f151f67
parentd6a617863761a4023b437bd70288abc77ac20554 (diff)
downloadrust-38c5af86a1412a29a367c856d83cd08dd7e92a33.tar.gz
rust-38c5af86a1412a29a367c856d83cd08dd7e92a33.zip
Add long diagnostics for E0373
-rw-r--r--src/librustc_borrowck/diagnostics.rs51
1 files changed, 50 insertions, 1 deletions
diff --git a/src/librustc_borrowck/diagnostics.rs b/src/librustc_borrowck/diagnostics.rs
index 3e7cfe3ee7f..4f90a287cb9 100644
--- a/src/librustc_borrowck/diagnostics.rs
+++ b/src/librustc_borrowck/diagnostics.rs
@@ -12,6 +12,56 @@
 
 register_long_diagnostics! {
 
+E0373: r##"
+This error occurs when an attempt is made to use data captured by a closure,
+when that data may no longer exist. It's most commonly seen when attempting to
+return a closure:
+
+```
+fn foo() -> Box<Fn(u32) -> u32> {
+    let x = 0u32;
+    Box::new(|y| x + y)
+}
+```
+
+Notice that `x` is stack-allocated by `foo()`. By default, Rust captures
+closed-over data by reference. This means that once `foo()` returns, `x` no
+longer exists. An attempt to access `x` within the closure would thus be unsafe.
+
+Another situation where this might be encountered is when spawning threads:
+
+```
+fn foo() {
+    let x = 0u32;
+    let y = 1u32;
+
+    let thr = std::thread::spawn(|| {
+        x + y
+    });
+}
+```
+
+Since our new thread runs in parallel, the stack frame containing `x` and `y`
+may well have disappeared by the time we try to use them. Even if we call
+`thr.join()` within foo (which blocks until `thr` has completed, ensuring the
+stack frame won't disappear), we will not succeed: the compiler cannot prove
+that this behaviour is safe, and so won't let us do it.
+
+The solution to this problem is usually to switch to using a `move` closure.
+This approach moves (or copies, where possible) data into the closure, rather
+than taking references to it. For example:
+
+```
+fn foo() -> Box<Fn(u32) -> u32> {
+    let x = 0u32;
+    Box::new(move |y| x + y)
+}
+```
+
+Now that the closure has its own copy of the data, there's no need to worry
+about safety.
+"##,
+
 E0381: r##"
 It is not allowed to use or capture an uninitialized variable. For example:
 
@@ -28,7 +78,6 @@ used.
 }
 
 register_diagnostics! {
-    E0373, // closure may outlive current fn, but it borrows {}, which is owned by current fn
     E0382, // use of partially/collaterally moved value
     E0383, // partial reinitialization of uninitialized structure
     E0384, // reassignment of immutable variable