Understanding Rust Error E0387
Introduction to Rust Error E0387
Rust Error E0387 occurs when an attempt is made to mutate or mutably reference data that a closure has captured immutably. This error was emitted by older versions of the Rust compiler.
Analyzing the Erroneous Code Example
In the given erroneous code example, the function 'foo' accepts a parameter of type 'Fn', which means that it can only capture its context immutably. When 'foo' is called within the 'mutable' and 'mut_addr' functions, the closures passed attempt to mutate the captured variable 'x', causing Error E0387. To better understand the error, let's examine the code:
#![allow(unused)]
fn main() {
// 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; });
}
}
Solutions to Rust Error E0387
To resolve Rust Error E0387, you have two options: either change the definition of 'foo' to accept 'FnMut' instead of 'Fn', so the closures can capture their context mutably, or use the 'Cell' or 'RefCell' types to achieve interior mutability through a shared reference.
1. Changing 'Fn' to 'FnMut':
If you can modify the 'foo' function, change the parameter type to 'FnMut', which allows mutable closure captures. The updated 'foo' definition should look like this:
fn foo<F: FnMut()>(f: F) { }
2. Using 'Cell' or 'RefCell' for Interior Mutability:
If changing the 'foo' definition is not an option, you can use 'Cell' or 'RefCell' types for interior mutability. In this case, we use 'Cell' to redefine the 'mutable' function:
use std::cell::Cell;
fn foo<F: Fn()>(f: F) { }
fn mutable() {
let x = Cell::new(0u32);
foo(|| x.set(2));
}
For more information, refer to the Rust API documentation for 'Cell' and 'RefCell'.