|
…
|
||
|---|---|---|
| .. | ||
| src | ||
| .cargo-checksum.json | ||
| azure-pipelines.yml | ||
| Cargo.toml | ||
| LICENSE-APACHE.txt | ||
| LICENSE-MIT.txt | ||
| README.md | ||
replace_with
Temporarily take ownership of a value at a mutable location, and replace it with a new value based on the old one.
This crate provides the function replace_with(), which is like std::mem::replace() except it allows the replacement value to be mapped from the original value.
See RFC 1736 for a lot of discussion as to its merits. It was never merged, and the desired ability to temporarily move out of &mut T doesn't exist yet, so this crate is my interim solution.
It's very akin to take_mut, though uses Drop instead of std::panic::catch_unwind() to react to unwinding, which avoids the optimisation barrier of calling the extern "C" __rust_maybe_catch_panic(). As such it's up to ∞x faster. The API also attempts to make slightly more explicit the behavior on panic – replace_with() accepts two closures such that aborting in the "standard case" where the mapping closure (FnOnce(T) -> T) panics (as take_mut::take() does) is avoided. If the second closure (FnOnce() -> T) panics, however, then it does indeed abort. The "abort on first panic" behaviour is available with replace_with_or_abort().
Example
Consider this motivating example:
enum States {
A(String),
B(String),
}
impl States {
fn poll(&mut self) {
// error[E0507]: cannot move out of borrowed content
*self = match *self {
// ^^^^^ cannot move out of borrowed content
States::A(a) => States::B(a),
States::B(a) => States::A(a),
};
}
}
Depending on context this can be quite tricky to work around. With this crate, however:
enum States {
A(String),
B(String),
}
impl States {
fn poll(&mut self) {
replace_with_or_abort(self, |self_| match self_ {
States::A(a) => States::B(a),
States::B(a) => States::A(a),
});
}
}
Huzzah!
no_std
To use replace_with with no_std you have to disable the std feature, which is active by default, by specifying your dependency to it like this:
# Cargo.toml
[dependencies.replace_with]
version = ...
default-features = false
features = []
...
The replace_with() & replace_with_or_default() functions are available on stable Rust both, with and without std.
The replace_with_or_abort() function however by default makes use of std::process::abort() which is not available with no_std.
As such replace_with will by default call core::intrinsics::abort() instead, which in turn requires nightly Rust.
Not everything is lost for stable no_std though, replace_with has one more trick up its sleeve:
panic = "abort"
If you define panic = abort in the [profile] section of your crate's Cargo.toml …
# Cargo.toml
[profile.debug]
panic = "abort"
[profile.release]
panic = "abort"
… and add the "panic_abort" feature to replace_with in the dependencies section of your crate's Cargo.toml …
# Cargo.toml
[dependencies.replace_with]
features = ["panic_abort"]
...
… the "panic_abort" feature enables the replace_with_or_abort_unchecked() function becomes on stable Rust as an unsafe function, a simple wrapper around ptr::write(dest, f(ptr::read(dest)));.
Word of caution: It is crucial to only ever use this function having defined panic = "abort", or else bad things may happen. It's up to you to uphold this invariant!
License
Licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE.txt or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT.txt or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.