Change selector in match when selector is a mutable reference - rust

I want to change enum variant based on some properties of the current enum variant in Iterator::next. I have two attempts, neither of which compile:
enum Test {
A(Vec<usize>),
B,
}
impl<'a> Iterator for Test {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
// attempt 1
if let Test::A(ref a) = *self {
if a.len() == 0 {
*self = Test::B; // doesn't work because a is borrowed
};
}
// attempt 2
*self = match *self {
Test::A(ref a) if a.len() == 0 => Test::B,
_ => *self, // cannot move out of borrowed context
};
None
}
}
fn main() {}
My second attempt does work if I am not working with references in the selector:
let mut a = Test::A(vec![]);
a = match a {
Test::A(ref a) if a.len() == 0 => Test::B,
_ => a,
};
This question is related to Is there a way to use match() in rust when modifying the selector?, but the solution proposed there is not generic: it only works if the same function is executed in both branches.
What is the Rustacean way to achieve my goal?

Since the condition is not very readable when put inside an if let / match block, I would just use a helper function to test for it:
impl Test {
fn is_empty_a(&self) -> bool {
if let Test::A(ref a) = *self {
a.len() == 0
} else {
false
}
}
}
And then there shouldn't be any borrowing issues:
impl<'a> Iterator for Test {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
if self.is_empty_a() {
*self = Test::B;
}
None
}
}

Related

Is there something similar to try_retain()?

Is it possible to apply FnMut(&mut V) -> Result<bool> for collection elements and if result is:
Ok(false) -- remove element and continue
Ok(true) -- leave element in collection and continue
Err(_) -- stop and return the error
Basically, how to code a try_retain(), an equivalent of this C++ code:
struct S {
// ...
};
// Updates `s` (and possibly some external state), returns true if `s` is no longer needed
// throws on error
bool bar(S& s);
void foo(std::map<int, S>& m) {
try
{
for(auto it = m.begin(), it_end = m.end(); it != it_end; ) {
if (bar(it->second))
m.erase(it++);
else
++it;
}
}
catch(...)
{
printf("bailed early\n");
throw;
}
}
For some reason I keep running into this pattern and I can't figure out how to do it in Rust without traversing collection twice or using additional memory...
This won't short-circuit, but you can do it by storing the global result in a mutable variable that's updated by the closure you pass to retain. Something like this:
use std::collections::HashMap;
fn try_retain<K, V, E, F>(m: &mut HashMap<K, V>, mut f: F) -> Result<(), E>
where
F: FnMut(&K, &mut V) -> Result<bool, E>,
{
let mut result = Ok(());
m.retain(|k, v| match f(k, v) {
Ok(b) => b,
Err(e) => {
result = Err(e);
true
}
});
return result;
}
Playground
If you're fine with switching to hashbrown::HashMap (which is what std::collections::HashMap uses as it's inner implementation), you could copy the implementation of fn retain and slightly change it to use the ? operator to return the first error:
fn try_retain<K, V, E, F>(m: &mut hashbrown::HashMap<K, V>, mut f: F) -> Result<(), E>
where
F: FnMut(&K, &mut V) -> Result<bool, E>,
{
let mut raw_table = m.raw_table();
// Here we only use `iter` as a temporary, preventing use-after-free
unsafe {
for item in raw_table.iter() {
let &mut (ref key, ref mut value) = item.as_mut();
if !f(key, value)? {
raw_table.erase(item);
}
}
}
Ok(())
}
This requires hashbrown's raw feature to be enabled, so you can access the underlying raw table.
This stops at the first error, and the elements are visited in a random order, so it's non-deterministic which elements are removed if an error occurs.
We could use try_for_each and remember the keys to remove (not to retain) till the end of the iteration because of the borrow checker:
fn try_retain(
map: &mut HashMap<i32, i32>,
to_retain: fn(k: i32, v: i32) -> bool,
) -> Result<bool, String> {
let mut to_remove: Vec<i32> = vec![];
map.iter().try_for_each(|(k, v)| {
if has_error() {
return Err("bailed early".to_string());
}
if !to_retain(*k, *v) {
to_remove.push(*k);
}
Ok::<_, String>(())
})?; // short-circuiting
if to_remove.is_empty() {
Ok(true)
} else {
(*map).retain(|&k, _| !to_remove.contains(&k));
Ok(false)
}
}
The return value results from whether there are keys to remove.
Can short-circuiting.
Playground
If the extra space for the to-remove-items is still too much, here is an unstable solution with drain_filter:
fn try_retain(
map: &mut HashMap<i32, i32>,
to_retain: fn(k: i32, v: i32) -> bool,
) -> bool {
let mut rslt = true;
map.drain_filter(|k, v| {
if !to_retain(*k, *v) {
rslt = false;
true
} else {
false
}
});
rslt
}
Short-circuiting is only possible through panic.
Playground

Pattern binding the same variable to different types sharing a trait

I have a question about pattern matching on values sharing some behaviour through a trait.
I have an enum with two variants, each binding value of different types, where both types implement a trait. I'm trying to figure out whether it's possible to create a single pattern (of the E::VarA(x) | E::VarB(x) form) in which I bind both types to a single constant, provided I'm only interested in using the shared behaviour.
An illustrative example: Playground:
trait T {
fn f(&self) -> usize;
}
struct A;
impl T for A {
fn f(&self) -> usize { 1 }
}
struct B;
impl T for B {
fn f(&self) -> usize { 2 }
}
enum E {
VarA(A),
VarB(B),
}
fn unwrap(e: E) -> usize {
match e {
E::VarA(v) | E::VarB(v) => T::f(&v)
}
}
fn main() {
let val = E::VarA(A{});
println!("{}", unwrap(val));
}
The code obviously does not compile, but it shows my intentions. Is there a way to make the code work, preferably more elegant than simply splitting the pat1 | pat2 => ... into pat1 => ... ; pat2 => ... ?
You can make a macro that unwraps to match statement.
trait T {
fn f(&self) -> usize;
}
struct A;
impl T for A {
fn f(&self) -> usize { 1 }
}
struct B;
impl T for B {
fn f(&self) -> usize { 2 }
}
enum E {
VarA(A),
VarB(B),
}
macro_rules! unwrap {
($value:expr, $pattern:pat => $result:expr) => {
match $value {
E::VarA($pattern) => $result,
E::VarB($pattern) => $result,
}
};
}
fn main() {
let a = E::VarA(A{});
let b = E::VarB(B{});
println!("a:{} b:{}",
unwrap!(a, ref sm => sm.f()),
unwrap!(b, ref sm => sm.f()));
}
If all variants implement this trait, the best solution is to implement the trait for the whole enum (playground).
Relevant code:
impl T for E {
fn f(&self) -> usize {
match self {
E::VarA(x) => x.f(),
E::VarB(x) => x.f(),
}
}
}

Is there a way of extracting a value from a &mut enum when the old one is discarded? [duplicate]

This question already has answers here:
How can I change enum variant while moving the field to the new variant?
(4 answers)
Closed 4 years ago.
I would like to extract a value from a pattern matched mutable reference and discard the old one.
This is a minimal example I've come up with:
fn main() {
#[derive(Debug)]
enum D<A> {
E1(A),
E2(A, A),
};
trait Zero<A> {
fn zero() -> A;
}
impl<A> D<A> {
pub fn push2(&mut self, e: A) {
match self {
D::E1(e1) => {
*self = D::E2(*e1, e);
}
_ => unimplemented!(),
}
}
pub fn push(&mut self, e: A)
where
A: Zero<A>,
{
match self {
D::E1(e1) => {
let mut r = A::zero();
std::mem::swap(&mut r, e1);
*self = D::E2(e, r);
}
_ => unimplemented!(),
};
}
}
impl Zero<i32> for i32 {
fn zero() -> i32 {
0
}
}
let mut d = D::E1(10);
d.push(11);
println!("{:?}", d);
}
playground
push2 fails with:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:17:39
|
17 | *self = D::E2(*e1, e);
| ^^^ cannot move out of borrowed content
I would like to achieve this without requiring the Copy trait as I have some boxed types inside and ideally would like to remove the need for the dummy variable (A::zero()).
If I understand your code, A will be some number because it implements Zero. The numeric values implement Copy in Rust, so you can use this to your advantage:
pub fn push(&mut self, e: A)
where
A: Zero<A> + Copy,
{
match self {
D::E1(e1) => *self = D::E2(e, *e1),
_ => unimplemented!(),
};
}
If you do not want to bound against Copy, you can use std::mem::replace as suggested:
pub fn push(&mut self, e: A)
where
A: Zero<A>,
{
use std::mem::replace;
match self {
D::E1(e1) => *self = D::E2(e, replace(e1, A::zero())),
_ => unimplemented!(),
};
}
That is the idiomatic way to do so.
You need the dummy element because you cannot do this:
D::E1(e1) => *self = D::E2(e, *e1),
The reason is that the compiler evaluates the parameters first, and *e1 tries to take the ownership of borrowed data, but that is forbidden.

Move mutable reference out of mutable object

I have an iterator over an enum that has a mutable reference in one of its variants. Now I want to move this reference out self and return it. To avoid having two mutable references to the same object at the same time, I want to change the enum variant of self to one that does not have the reference. Below is a code example:
enum Test<'a> {
A(&'a mut usize),
B,
}
impl<'a> Iterator for Test<'a> {
type Item = &'a mut usize;
fn next(&mut self) -> Option<Self::Item> {
match *self {
Test::A(r) => Some(r), // TODO: return mutable reference and change self to B
Test::B => None,
}
}
}
fn main() {
let mut v = 1;
let mut it = Test::A(&mut v);
it.next();
}
The question is related to Change selector in match when selector is a mutable reference, but that solution does not work here.
I would use a swap:
fn next(&mut self) -> Option<Self::Item> {
if let Test::A(_) = *self {
let mut to_swap = Test::B;
std::mem::swap(self, &mut to_swap);
match to_swap {
Test::A(r) => Some(r),
_ => unreachable!(), // never reached
}
} else {
None
}
}
You can use an helper function as follow:
impl<'a> Iterator for Test<'a> {
type Item = &'a mut usize;
fn next(&mut self) -> Option<Self::Item> {
if let Test::A(_) = *self {
let mut to_swap = Test::B;
std::mem::swap(self, &mut to_swap);
to_swap.consume_as_a()
} else {
None
}
}
}
impl<'a> Test<'a> {
fn consume_as_a(self) -> Option<&'a mut usize> {
match self {
Test::A(r) => Some(r),
_ => None,
}
}
}

Elegant way to borrow and return a mutable reference in Rust

I'm trying to return a mutable reference after doing some operation on it. This is best explained by a piece of code:
#[derive(PartialEq)]
pub enum Value {
Null,
Array(Vec<Value>),
}
impl Value {
pub fn new() -> Value {
Value::Array(Vec::new())
}
pub fn push<'a, T> (&'a mut self, value: T) -> Option<&'a mut Value>
where T:Into<Value> {
let temp = match *self {
Value::Array(ref mut vec) => {
vec.push(value.into());
true
},
_ => false,
};
if temp {
Some(self)
} else {
None
}
}
}
#[test]
fn push_test() {
let mut val = Value::new();
val.push(Value::Null);
assert!(val == Value::Array(vec![Value::Null]));
}
The play version is here. The workaround with boolean values is because I would be borrowing multiple times if I return Some(self) from within the match block. Is there an elegant way to implement the push function without using boolean values? If its possible to retain the function signature then its a bonus. Thank you!
The workaround with boolean values is because I would be borrowing multiple times if I return Some(self) from within the match block
Another option is to replace self temporally, so v can take the ownership of the vector (avoiding the borrow). After adding the new item to v, we reconstruct the self value:
// the lifetime 'a can be omitted
pub fn push<T>(&mut self, value: T) -> Option<&mut Value>
where T: Into<Value>
{
// replace put Value::Null on self and return the old value
match ::std::mem::replace(self, Value::Null) {
Value::Array(mut v) => {
v.push(value.into());
*self = Value::Array(v);
Some(self)
},
_ => None,
}
}

Resources