Use .take instead of mem::take().

This commit is contained in:
Stephen Chung 2023-04-21 10:20:19 +08:00
parent 2034ddd830
commit f49ce33a88
14 changed files with 72 additions and 57 deletions

View File

@ -19,6 +19,7 @@ New features
* It is now possible to require a specific _type_ to the `this` pointer for a particular script-defined function so that it is called only when the `this` pointer contains the specified type.
* `is_def_fn` is extended to support checking for typed methods, with syntax `is_def_fn(this_type, fn_name, arity)`
* `Dynamic::take` is added as a short-cut for `std::mem::take(&mut value)`.
Enhancements
------------

View File

@ -124,7 +124,7 @@ impl Engine {
///
/// To access a primary argument value (i.e. cloning is cheap), use: `args[n].as_xxx().unwrap()`
///
/// To access an argument value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
/// To access an argument value and avoid cloning, use `args[n].take().cast::<T>()`.
/// Notice that this will _consume_ the argument, replacing it with `()`.
///
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()`

View File

@ -16,6 +16,7 @@ use std::{
fmt::Write,
hash::Hash,
iter::once,
mem,
num::{NonZeroU8, NonZeroUsize},
};
@ -833,6 +834,11 @@ impl Expr {
Self::Property(..) => matches!(token, Token::LeftParen),
}
}
/// Return this [`Expr`], replacing it with [`Expr::Unit`].
#[inline(always)]
pub fn take(&mut self) -> Self {
mem::take(self)
}
/// Recursively walk this expression.
/// Return `false` from the callback to terminate the walk.
pub fn walk<'a>(

View File

@ -1036,6 +1036,11 @@ impl Stmt {
pub const fn is_control_flow_break(&self) -> bool {
matches!(self, Self::Return(..) | Self::BreakLoop(..))
}
/// Return this [`Stmt`], replacing it with [`Stmt::Noop`].
#[inline(always)]
pub fn take(&mut self) -> Self {
mem::take(self)
}
/// Recursively walk this statement.
/// Return `false` from the callback to terminate the walk.
pub fn walk<'a>(

View File

@ -939,7 +939,7 @@ impl Engine {
if !val.is_shared() {
// Replace the variable with a shared value.
*val = std::mem::take(val).into_shared();
*val = val.take().into_shared();
}
}

View File

@ -748,7 +748,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
return match op {
PlusAssign => Some((
|_ctx, args| {
let x = std::mem::take(args[1]).into_array().unwrap();
let x = args[1].take().into_array().unwrap();
if x.is_empty() {
return Ok(Dynamic::UNIT);
@ -783,7 +783,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
return match op {
PlusAssign => Some((
|_ctx, args| {
let blob2 = std::mem::take(args[1]).into_blob().unwrap();
let blob2 = args[1].take().into_blob().unwrap();
let blob1 = &mut *args[0].write_lock::<Blob>().unwrap();
#[cfg(not(feature = "unchecked"))]
@ -931,7 +931,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
PlusAssign => Some((
|_ctx, args| {
{
let x = std::mem::take(args[1]);
let x = args[1].take();
let array = &mut *args[0].write_lock::<Array>().unwrap();
push(array, x);
}

View File

@ -831,7 +831,7 @@ impl Engine {
}
// FnPtr call on object
let fn_ptr = mem::take(&mut call_args[0]).cast::<FnPtr>();
let fn_ptr = call_args[0].take().cast::<FnPtr>();
#[cfg(not(feature = "no_function"))]
let (fn_name, is_anon, fn_curry, _environ, fn_def) = {

View File

@ -57,12 +57,12 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
}
if TypeId::of::<T>() == TypeId::of::<String>() {
// If T is `String`, data must be `ImmutableString`, so map directly to it
return reify! { mem::take(data).into_string().expect("`ImmutableString`") => T };
return reify! { data.take().into_string().expect("`ImmutableString`") => T };
}
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
mem::take(data).cast::<T>()
data.take().cast::<T>()
}
/// Trait to register custom Rust functions.

View File

@ -67,7 +67,7 @@ impl Engine {
// Put arguments into scope as variables
scope.extend(fn_def.params.iter().cloned().zip(args.iter_mut().map(|v| {
// Actually consume the arguments instead of cloning them
mem::take(*v)
v.take()
})));
// Push a new call stack frame

View File

@ -1204,7 +1204,7 @@ impl Module {
///
/// To access a primary argument value (i.e. cloning is cheap), use: `args[n].as_xxx().unwrap()`
///
/// To access an argument value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
/// To access an argument value and avoid cloning, use `args[n].take().cast::<T>()`.
/// Notice that this will _consume_ the argument, replacing it with `()`.
///
/// To access the first mutable argument, use `args.get_mut(0).unwrap()`
@ -1227,7 +1227,7 @@ impl Module {
/// // 'args' is guaranteed to be the right length and of the correct types
///
/// // Get the second parameter by 'consuming' it
/// let double = std::mem::take(args[1]).cast::<bool>();
/// let double = args[1].take().cast::<bool>();
/// // Since it is a primary type, it can also be cheaply copied
/// let double = args[1].clone_cast::<bool>();
/// // Get a mutable reference to the first argument.

View File

@ -184,7 +184,7 @@ fn optimize_stmt_block(
|s| matches!(s, Stmt::Block(block, ..) if !block.iter().any(Stmt::is_block_dependent)),
) {
let (first, second) = statements.split_at_mut(n);
let stmt = mem::take(&mut second[0]);
let stmt = second[0].take();
let mut stmts = match stmt {
Stmt::Block(block, ..) => block,
stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt),
@ -406,7 +406,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
Expr::FnCall(ref mut x2, pos) => {
state.set_dirty();
x.0 = OpAssignment::new_op_assignment_from_base(&x2.name, pos);
x.1.rhs = mem::take(&mut x2.args[1]);
x.1.rhs = x2.args[1].take();
}
ref expr => unreachable!("Expr::FnCall expected but gets {:?}", expr),
}
@ -426,7 +426,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
state.set_dirty();
let pos = condition.start_position();
let mut expr = mem::take(condition);
let mut expr = condition.take();
optimize_expr(&mut expr, state, false);
*stmt = if preserve_result {
@ -456,7 +456,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// if false { if_block } else { else_block } -> else_block
Stmt::If(x, ..) if matches!(x.expr, Expr::BoolConstant(false, ..)) => {
state.set_dirty();
let body = mem::take(&mut *x.branch);
let body = x.branch.take_statements();
*stmt = match optimize_stmt_block(body, state, preserve_result, true, false) {
statements if statements.is_empty() => Stmt::Noop(x.branch.position()),
statements => (statements, x.branch.span()).into(),
@ -465,7 +465,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// if true { if_block } else { else_block } -> if_block
Stmt::If(x, ..) if matches!(x.expr, Expr::BoolConstant(true, ..)) => {
state.set_dirty();
let body = mem::take(&mut *x.body);
let body = x.body.take_statements();
*stmt = match optimize_stmt_block(body, state, preserve_result, true, false) {
statements if statements.is_empty() => Stmt::Noop(x.body.position()),
statements => (statements, x.body.span()).into(),
@ -475,9 +475,9 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
Stmt::If(x, ..) => {
let FlowControl { expr, body, branch } = &mut **x;
optimize_expr(expr, state, false);
let statements = mem::take(&mut **body);
let statements = body.take_statements();
**body = optimize_stmt_block(statements, state, preserve_result, true, false);
let statements = mem::take(&mut **branch);
let statements = branch.take_statements();
**branch = optimize_stmt_block(statements, state, preserve_result, true, false);
}
@ -508,7 +508,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
if b.is_always_true() {
// Promote the matched case
let mut statements = Stmt::Expr(mem::take(&mut b.expr).into());
let mut statements = Stmt::Expr(b.expr.take().into());
optimize_stmt(&mut statements, state, true);
*stmt = statements;
} else {
@ -518,14 +518,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
let branch = match def_case {
Some(index) => {
let mut def_stmt =
Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
Stmt::Expr(expressions[*index].expr.take().into());
optimize_stmt(&mut def_stmt, state, true);
def_stmt.into()
}
_ => StmtBlock::NONE,
};
let body = Stmt::Expr(mem::take(&mut b.expr).into()).into();
let expr = mem::take(&mut b.condition);
let body = Stmt::Expr(b.expr.take().into()).into();
let expr = b.condition.take();
*stmt = Stmt::If(
FlowControl { expr, body, branch }.into(),
@ -542,7 +542,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
if b.is_always_true() {
// Promote the matched case
let mut statements = Stmt::Expr(mem::take(&mut b.expr).into());
let mut statements = Stmt::Expr(b.expr.take().into());
optimize_stmt(&mut statements, state, true);
*stmt = statements;
state.set_dirty();
@ -567,11 +567,11 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
if range_block.is_always_true() {
// Promote the matched case
let block = &mut expressions[r.index()];
let mut statements = Stmt::Expr(mem::take(&mut block.expr).into());
let mut statements = Stmt::Expr(block.expr.take().into());
optimize_stmt(&mut statements, state, true);
*stmt = statements;
} else {
let mut expr = mem::take(&mut range_block.condition);
let mut expr = range_block.condition.take();
// switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def }
optimize_expr(&mut expr, state, false);
@ -579,16 +579,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
let branch = match def_case {
Some(index) => {
let mut def_stmt =
Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
Stmt::Expr(expressions[*index].expr.take().into());
optimize_stmt(&mut def_stmt, state, true);
def_stmt.into()
}
_ => StmtBlock::NONE,
};
let body =
Stmt::Expr(mem::take(&mut expressions[r.index()].expr).into())
.into();
let body = Stmt::Expr(expressions[r.index()].expr.take().into()).into();
*stmt = Stmt::If(
FlowControl { expr, body, branch }.into(),
@ -628,7 +626,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
match def_case {
Some(index) => {
let mut def_stmt = Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
let mut def_stmt = Stmt::Expr(expressions[*index].expr.take().into());
optimize_stmt(&mut def_stmt, state, true);
*stmt = def_stmt;
}
@ -738,17 +736,17 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
if let Expr::BoolConstant(true, pos) = expr {
*expr = Expr::Unit(*pos);
}
**body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false);
**body = optimize_stmt_block(body.take_statements(), state, false, true, false);
}
// do { block } while|until expr
Stmt::Do(x, ..) => {
optimize_expr(&mut x.expr, state, false);
*x.body = optimize_stmt_block(mem::take(&mut *x.body), state, false, true, false);
*x.body = optimize_stmt_block(x.body.take_statements(), state, false, true, false);
}
// for id in expr { block }
Stmt::For(x, ..) => {
optimize_expr(&mut x.2.expr, state, false);
*x.2.body = optimize_stmt_block(mem::take(&mut *x.2.body), state, false, true, false);
*x.2.body = optimize_stmt_block(x.2.body.take_statements(), state, false, true, false);
}
// let id = expr;
Stmt::Var(x, options, ..) if !options.contains(ASTFlags::CONSTANT) => {
@ -771,7 +769,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// Only one statement which is not block-dependent - promote
[s] if !s.is_block_dependent() => {
state.set_dirty();
*stmt = mem::take(s);
*stmt = s.take();
}
_ => *stmt = (block, span).into(),
}
@ -781,15 +779,15 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// If try block is pure, there will never be any exceptions
state.set_dirty();
*stmt = (
optimize_stmt_block(mem::take(&mut *x.body), state, false, true, false),
optimize_stmt_block(x.body.take_statements(), state, false, true, false),
x.body.span(),
)
.into();
}
// try { try_block } catch ( var ) { catch_block }
Stmt::TryCatch(x, ..) => {
*x.body = optimize_stmt_block(mem::take(&mut *x.body), state, false, true, false);
*x.branch = optimize_stmt_block(mem::take(&mut *x.branch), state, false, true, false);
*x.body = optimize_stmt_block(x.body.take_statements(), state, false, true, false);
*x.branch = optimize_stmt_block(x.branch.take_statements(), state, false, true, false);
}
// expr(stmt)
@ -799,7 +797,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
Expr::Stmt(block) if !block.is_empty() => {
let mut stmt_block = *mem::take(block);
*stmt_block =
optimize_stmt_block(mem::take(&mut *stmt_block), state, true, true, false);
optimize_stmt_block(stmt_block.take_statements(), state, true, true, false);
*stmt = stmt_block.into();
}
Expr::Stmt(..) => *stmt = Stmt::Noop(expr.position()),
@ -810,7 +808,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// expr(func())
Stmt::Expr(expr) if matches!(**expr, Expr::FnCall(..)) => {
state.set_dirty();
match mem::take(expr.as_mut()) {
match expr.take() {
Expr::FnCall(x, pos) => *stmt = Stmt::FnCall(x, pos),
_ => unreachable!(),
}
@ -820,7 +818,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// func(...)
Stmt::FnCall(..) => {
if let Stmt::FnCall(x, pos) = mem::take(stmt) {
if let Stmt::FnCall(x, pos) = stmt.take() {
let mut expr = Expr::FnCall(x, pos);
optimize_expr(&mut expr, state, false);
*stmt = match expr {
@ -883,16 +881,16 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
}
// { stmt; ... } - do not count promotion as dirty because it gets turned back into an array
Expr::Stmt(x) => {
***x = optimize_stmt_block(mem::take(&mut **x), state, true, true, false);
***x = optimize_stmt_block(x.take_statements(), state, true, true, false);
// { Stmt(Expr) } - promote
if let [ Stmt::Expr(e) ] = &mut ****x { state.set_dirty(); *expr = mem::take(e); }
if let [ Stmt::Expr(e) ] = &mut ****x { state.set_dirty(); *expr = e.take(); }
}
// ()?.rhs
#[cfg(not(feature = "no_object"))]
Expr::Dot(x, options, ..) if options.contains(ASTFlags::NEGATED) && matches!(x.lhs, Expr::Unit(..)) => {
state.set_dirty();
*expr = mem::take(&mut x.lhs);
*expr = x.lhs.take();
}
// lhs.rhs
#[cfg(not(feature = "no_object"))]
@ -935,7 +933,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
#[cfg(not(feature = "no_index"))]
Expr::Index(x, options, ..) if options.contains(ASTFlags::NEGATED) && matches!(x.lhs, Expr::Unit(..)) => {
state.set_dirty();
*expr = mem::take(&mut x.lhs);
*expr = x.lhs.take();
}
// lhs[rhs]
#[cfg(not(feature = "no_index"))]
@ -946,7 +944,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
// Array literal where everything is pure - promote the indexed item.
// All other items can be thrown away.
state.set_dirty();
let mut result = mem::take(&mut a[*i as usize]);
let mut result = a[*i as usize].take();
result.set_position(*pos);
*expr = result;
}
@ -956,7 +954,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
// All other items can be thrown away.
state.set_dirty();
let index = a.len() - i.unsigned_abs() as usize;
let mut result = mem::take(&mut a[index]);
let mut result = a[index].take();
result.set_position(*pos);
*expr = result;
}
@ -1018,7 +1016,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
// Merge consecutive strings
while n < x.len() - 1 {
match (mem::take(&mut x[n]), mem::take(&mut x[n+1])) {
match (x[n].take(),x[n+1].take()) {
(Expr::StringConstant(mut s1, pos), Expr::StringConstant(s2, ..)) => { s1 += s2; x[n] = Expr::StringConstant(s1, pos); x.remove(n+1); state.set_dirty(); }
(expr1, Expr::Unit(..)) => { x[n] = expr1; x.remove(n+1); state.set_dirty(); }
(Expr::Unit(..), expr2) => { x[n+1] = expr2; x.remove(n); state.set_dirty(); }
@ -1051,34 +1049,34 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
// lhs && rhs
Expr::And(x, ..) => match (&mut x.lhs, &mut x.rhs) {
// true && rhs -> rhs
(Expr::BoolConstant(true, ..), rhs) => { state.set_dirty(); optimize_expr(rhs, state, false); *expr = mem::take(rhs); }
(Expr::BoolConstant(true, ..), rhs) => { state.set_dirty(); optimize_expr(rhs, state, false); *expr = rhs.take(); }
// false && rhs -> false
(Expr::BoolConstant(false, pos), ..) => { state.set_dirty(); *expr = Expr::BoolConstant(false, *pos); }
// lhs && true -> lhs
(lhs, Expr::BoolConstant(true, ..)) => { state.set_dirty(); optimize_expr(lhs, state, false); *expr = mem::take(lhs); }
(lhs, Expr::BoolConstant(true, ..)) => { state.set_dirty(); optimize_expr(lhs, state, false); *expr = lhs.take(); }
// lhs && rhs
(lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, false); }
},
// lhs || rhs
Expr::Or(ref mut x, ..) => match (&mut x.lhs, &mut x.rhs) {
// false || rhs -> rhs
(Expr::BoolConstant(false, ..), rhs) => { state.set_dirty(); optimize_expr(rhs, state, false); *expr = mem::take(rhs); }
(Expr::BoolConstant(false, ..), rhs) => { state.set_dirty(); optimize_expr(rhs, state, false); *expr = rhs.take(); }
// true || rhs -> true
(Expr::BoolConstant(true, pos), ..) => { state.set_dirty(); *expr = Expr::BoolConstant(true, *pos); }
// lhs || false
(lhs, Expr::BoolConstant(false, ..)) => { state.set_dirty(); optimize_expr(lhs, state, false); *expr = mem::take(lhs); }
(lhs, Expr::BoolConstant(false, ..)) => { state.set_dirty(); optimize_expr(lhs, state, false); *expr = lhs.take(); }
// lhs || rhs
(lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, false); }
},
// () ?? rhs -> rhs
Expr::Coalesce(x, ..) if matches!(x.lhs, Expr::Unit(..)) => {
state.set_dirty();
*expr = mem::take(&mut x.rhs);
*expr = x.rhs.take();
},
// lhs:constant ?? rhs -> lhs
Expr::Coalesce(x, ..) if x.lhs.is_constant() => {
state.set_dirty();
*expr = mem::take(&mut x.lhs);
*expr = x.lhs.take();
},
// !true or !false
@ -1383,7 +1381,7 @@ impl Engine {
functions.into_iter().for_each(|fn_def| {
let mut fn_def = crate::func::shared_take_or_clone(fn_def);
// Optimize the function body
let body = mem::take(&mut *fn_def.body);
let body = fn_def.body.take_statements();
*fn_def.body = self.optimize_top_level(body, scope, lib2, optimization_level);

View File

@ -1146,6 +1146,11 @@ impl Dynamic {
)),
}
}
/// Return this [`Dynamic`], replacing it with [`Dynamic::UNIT`].
#[inline(always)]
pub fn take(&mut self) -> Self {
mem::take(self)
}
/// Convert the [`Dynamic`] value into specific type.
///
/// Casting to a [`Dynamic`] just returns as is, but if it contains a shared value,

View File

@ -175,7 +175,7 @@ fn test_fn_ptr_raw() -> Result<(), Box<EvalAltResult>> {
TypeId::of::<INT>(),
],
move |context, args| {
let fp = std::mem::take(args[1]).cast::<FnPtr>();
let fp = args[1].take().cast::<FnPtr>();
let value = args[2].clone();
let this_ptr = args.get_mut(0).unwrap();

View File

@ -16,8 +16,8 @@ fn test_fn_ptr_curry_call() -> Result<(), Box<EvalAltResult>> {
"call_with_arg",
[TypeId::of::<FnPtr>(), TypeId::of::<INT>()],
|context, args| {
let fn_ptr = std::mem::take(args[0]).cast::<FnPtr>();
fn_ptr.call_raw(&context, None, [std::mem::take(args[1])])
let fn_ptr = args[0].take().cast::<FnPtr>();
fn_ptr.call_raw(&context, None, [args[1].take()])
},
);