2015-04-09 14:49:03 -04:00
|
|
|
// Copyright 2014 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.
|
|
|
|
|
|
|
|
#![allow(non_snake_case)]
|
|
|
|
|
2015-05-19 16:53:34 -05:00
|
|
|
register_long_diagnostics! {
|
|
|
|
|
2015-07-23 10:44:04 +01:00
|
|
|
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.
|
|
|
|
"##,
|
|
|
|
|
2015-05-19 16:53:34 -05:00
|
|
|
E0381: r##"
|
|
|
|
It is not allowed to use or capture an uninitialized variable. For example:
|
|
|
|
|
|
|
|
```
|
|
|
|
fn main() {
|
|
|
|
let x: i32;
|
|
|
|
let y = x; // error, use of possibly uninitialized variable
|
|
|
|
```
|
|
|
|
|
|
|
|
To fix this, ensure that any declared variables are initialized before being
|
|
|
|
used.
|
2015-08-02 15:30:06 -04:00
|
|
|
"##,
|
|
|
|
|
|
|
|
E0384: r##"
|
|
|
|
This error occurs when an attempt is made to reassign an immutable variable.
|
|
|
|
For example:
|
|
|
|
|
|
|
|
```
|
|
|
|
fn main(){
|
|
|
|
let x = 3;
|
|
|
|
x = 5; // error, reassignment of immutable variable
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
By default, variables in Rust are immutable. To fix this error, add the keyword
|
|
|
|
`mut` after the keyword `let` when declaring the variable. For example:
|
|
|
|
|
|
|
|
```
|
|
|
|
fn main(){
|
|
|
|
let mut x = 3;
|
|
|
|
x = 5;
|
|
|
|
}
|
|
|
|
```
|
2015-08-08 17:32:13 +01:00
|
|
|
"##,
|
|
|
|
|
|
|
|
E0387: r##"
|
|
|
|
This error occurs when an attempt is made to mutate or mutably reference data
|
|
|
|
that a closure has captured immutably. Examples of this error are shown below:
|
|
|
|
|
|
|
|
```
|
|
|
|
// Accepts a function or a closure that captures its environment immutably.
|
|
|
|
// Closures passed to foo will not be able to mutate their closed-over state.
|
|
|
|
fn foo<F: Fn()>(f: F) { }
|
|
|
|
|
|
|
|
// Attempts to mutate closed-over data. Error message reads:
|
|
|
|
// `cannot assign to data in a captured outer variable...`
|
|
|
|
fn mutable() {
|
|
|
|
let mut x = 0u32;
|
|
|
|
foo(|| x = 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attempts to take a mutable reference to closed-over data. Error message
|
|
|
|
// reads: `cannot borrow data mutably in a captured outer variable...`
|
|
|
|
fn mut_addr() {
|
|
|
|
let mut x = 0u32;
|
|
|
|
foo(|| { let y = &mut x; });
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The problem here is that foo is defined as accepting a parameter of type `Fn`.
|
|
|
|
Closures passed into foo will thus be inferred to be of type `Fn`, meaning that
|
|
|
|
they capture their context immutably.
|
|
|
|
|
2015-08-10 16:48:01 +01:00
|
|
|
If the definition of `foo` is under your control, the simplest solution is to
|
|
|
|
capture the data mutably. This can be done by defining `foo` to take FnMut
|
|
|
|
rather than Fn:
|
2015-08-08 17:32:13 +01:00
|
|
|
|
|
|
|
```
|
|
|
|
fn foo<F: FnMut()>(f: F) { }
|
|
|
|
```
|
2015-08-10 16:48:01 +01:00
|
|
|
|
|
|
|
Alternatively, we can consider using the `Cell` and `RefCell` types to achieve
|
|
|
|
interior mutability through a shared reference. Our example's `mutable` function
|
|
|
|
could be redefined as below:
|
|
|
|
|
|
|
|
```
|
|
|
|
fn mutable() {
|
|
|
|
let x = std::cell::Cell::new(0u32);
|
|
|
|
foo(|| x.set(2));
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
You can read more about cell types in the API documentation:
|
|
|
|
|
|
|
|
https://doc.rust-lang.org/std/cell/
|
2015-05-19 16:53:34 -05:00
|
|
|
"##
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-04-09 14:49:03 -04:00
|
|
|
register_diagnostics! {
|
2015-05-19 16:53:34 -05:00
|
|
|
E0382, // use of partially/collaterally moved value
|
|
|
|
E0383, // partial reinitialization of uninitialized structure
|
|
|
|
E0385, // {} in an aliasable location
|
|
|
|
E0386, // {} in an immutable container
|
|
|
|
E0388, // {} in a static location
|
|
|
|
E0389 // {} in a `&` reference
|
2015-04-09 14:49:03 -04:00
|
|
|
}
|