Crates.io | replace_with |
lib.rs | replace_with |
version | 0.1.7 |
source | src |
created_at | 2018-11-08 20:55:34.665629 |
updated_at | 2020-08-14 09:14:40.80978 |
description | Temporarily take ownership of a value at a mutable location, and replace it with a new value based on the old one. |
homepage | https://github.com/alecmocatta/replace_with |
repository | https://github.com/alecmocatta/replace_with |
max_upload_size | |
id | 95570 |
size | 40,341 |
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()
.
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:
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!
Licensed under either of
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.