Matching a rust Enum with recursive definition using Rc - rust

The following is my test code.
use std::rc::Rc;
enum Node {
Cons(i32, Rc<Node>),
Nil,
}
fn main() {
let list = Rc::new(Node::Cons(1, Rc::new(Node::Nil)));
match &*list {
Node::Cons(value, _next) => {
println!("value: {}", value);
}
Node::Nil => {
println!("nil");
}
}
}
My question is why do I need to use & in the match statement? If I remove the & I will get the following compilation error:
error[E0507]: cannot move out of an `Rc`
--> src/main.rs:11:11
|
11 | match *list {
| ^^^^^ help: consider borrowing here: `&*list`
12 | Node::Cons(value, _next) => {
| -----
| |
| data moved here
| move occurs because `_next` has type `Rc<Node>`, which does not implement the `Copy` trait
I don't understand the error message above. I think since list is an Rc pointer, I should dereference list to match the Node variable. On the other hand, if I remove the recursive definition then in Node (just keeping the i32) or use Box instead of Rc then I am able to match *list.

First, let's give the easy solution: as you don't need the next, you can just not try to get it:
match *list {
Node::Cons(value, _) => {
println!("value: {}", value);
}
Node::Nil => {
println!("nil");
}
}
This also hints at why your code doesn't work without &: you're requesting the node to be moved into its components, the second one not being Copy, which means the node has first to be moved out of its Rc.
But you don't normally move a value out of its Rc: a Rc is supposed to be shared so you can't remove the value without checking first there's no other smart reference to it.
If you really want to remove the node from the Rc, you can do
if let Ok(node) = Rc::<Node>::try_unwrap(list) {
match node {
Node::Cons(value, next) => {
println!("value: {}", value);
// you may use next here
}
Node::Nil => {
println!("nil");
}
}
}
But most often, if you have a Rc, what you want is either to look at it (as your code does with the &) or try get a mutable reference with get_mut which will work only if there's no other reference to it.

Related

How can I iteratively traverse a tree built with Rc<RefCell<T>> while mutably matching on enums?

I cannot find a way to do simple tree traversal without recursion within the guidelines of the borrow checker.
Advent of Code 2021 Day 18 has numbers that can be represented either by a pair of numbers or by a value (perfect for Rust enums). There are also parts of the problem where I'm using a stack to traverse the tree so I'm using Rc<RefCell<T>> to represent child nodes. This led me to the following representation:
use std::{cell::RefCell, rc::Rc};
enum Number {
Pair(Rc<RefCell<Number>>, Rc<RefCell<Number>>),
Value(u8),
}
I'm trying to make a function that updates the value of the left-most value in a Number. Writing the recursive version took a few tries but ended up working. However, I cannot get an iterative version to compile no matter what I try. The combination of matching the node and having to borrow with RefMut leads me to borrow/lifetime problems. This is what I'm trying to do:
fn propagate_left(mut node: &Rc<RefCell<Number>>, val: u8) {
loop {
let mut ref_mut = node.borrow_mut();
match &mut (*ref_mut) {
Number::Pair(left_node, _) => {
node = left_node;
}
Number::Value(number_val) => {
*number_val += val;
return;
}
}
}
}
error[E0597]: `ref_mut` does not live long enough
--> src/lib.rs:11:22
|
8 | fn propagate_left(mut node: &Rc<RefCell<Number>>, val: u8) {
| - let's call the lifetime of this reference `'1`
...
11 | match &mut (*ref_mut) {
| ^^^^^^^ borrowed value does not live long enough
12 | Number::Pair(left_node, _) => {
| --------- assignment requires that `ref_mut` is borrowed for `'1`
...
20 | }
| - `ref_mut` dropped here while still borrowed
Is there any way to make this work without using unsafe code or recursion?
I used a Vec for storage and indices in my structures to avoid this kind of problem in other puzzles but I wanted to try with references in this one.
The problem with your attempt is that the lifetime of the return value of node.borrow_mut() is tied to node, but you are attempting to store that result into a variable that has a caller-provided lifetime. When the current iteration's borrow_mut() ends, the reference (used by left_node) expires and you cannot store something with a shorter lifetime into a variable with a longer lifetime — the entire point of memory safety.
A workaround is to avoid concurrently using references (&) and reference counting (Rc). Using only Rc, you can clone the child and replace the temporary variable:
use std::{cell::RefCell, rc::Rc};
enum Number {
Pair(Rc<RefCell<Number>>, Rc<RefCell<Number>>),
Value(u8),
}
fn propagate_left(mut node: Rc<RefCell<Number>>, val: u8) {
loop {
let next = match &mut *node.borrow_mut() {
Number::Pair(left_node, _) => Rc::clone(left_node),
Number::Value(number_val) => {
*number_val += val;
return;
}
};
node = next;
}
}
You can also use only references and avoid Rc / RefCell completely:
enum Number {
Pair(Box<Number>, Box<Number>),
Value(u8),
}
fn propagate_left(mut node: &mut Number, val: u8) {
loop {
match node {
Number::Pair(left_node, _) => {
node = left_node;
}
Number::Value(number_val) => {
*number_val += val;
return;
}
};
}
}
See also:
Borrowed value does not live long enough when adding to a binary tree built on RefCell
My solution to AoC 2021 Day 18
Cannot obtain a mutable reference when iterating a recursive structure: cannot borrow as mutable more than once at a time
Adding an append method to a singly linked list
How do I keep a mutable reference to the last node while building a linked list?

How to use methods within match statements before using the match arm's associated value?

I'm looking for a workaround/solution to a problem I've encountered at least a couple times. It occurs when matching to an enum member of a struct, where depending on the match, different (mutating) methods may be called on the struct before the enum's associated value is used. The methods require a mutable reference to the struct, which disallows usage of the enum's associated value afterwards. Trivial example:
struct NonCopyType {
foo: u32
}
enum TwoVariants {
V1(NonCopyType),
V2(NonCopyType)
}
struct VariantHolder {
var: TwoVariants
}
impl VariantHolder {
fn double(&mut self) {
match &mut self.var {
TwoVariants::V1(v) => {
v.foo *= 2;
},
TwoVariants::V2(v) => {
v.foo *= 2;
}
}
}
}
fn main() {
let var = TwoVariants::V1( NonCopyType {
foo: 1
});
let mut holder = VariantHolder {
var
};
match &mut holder.var {
TwoVariants::V1(v) => {
holder.double();
println!("{}", v.foo); // Problem here
},
_ => ()
}
}
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5a9f8643546d08878bb5fabe5703d889
This can't be allowed because the variant may have been changed and v may not even make sense as a value any more. This occurs even if the method does not modify the variant at all; as long as you have to call a mutable method for any reason before using the enum's associated value (say, to mutate a different member which is used in a calculation with the associated value), the borrow checker errors out compilation.
There are two workarounds I can think of. First is to use a second match statement, separating the method calls from the value usage. This I don't like because it separates the logic. Second is to use a nested if let within the match arm. This is a little better, even if by then I'm 3 tabs deep for what should be a a relatively simple operation.
However, my preferred solution would be to not rematch against the enum at all. Is there a way I can leverage unsafe to access the enum's associated value without checking the variant? (or any way at all I can avoid rematching after calling a mutating method?)
Understanding the Problem
Compilation of your code yields:
error[E0499]: cannot borrow `holder` as mutable more than once at a time
--> temp.rs:38:13
|
36 | match &mut holder.var {
| --------------- first mutable borrow occurs here
37 | TwoVariants::V1(v) => {
38 | holder.double();
| ^^^^^^ second mutable borrow occurs here
39 | println!("{}", v.foo); // Problem here
| ----- first borrow later used here
So we cannot have 2 mutable borrows. Observe that we are not modifying holder.var, we can get away with an immutable reference. Change the match &mut holder.var into match &holder.var and compile, we get:
error[E0502]: cannot borrow `holder` as mutable because it is also borrowed as immutab
le
--> temp.rs:38:13
|
36 | match &holder.var {
| ----------- immutable borrow occurs here
37 | TwoVariants::V1(v) => {
38 | holder.double();
| ^^^^^^^^^^^^^^^ mutable borrow occurs here
39 | println!("{}", v.foo); // Problem here
| ----- immutable borrow later used here
So the compiler prevents us from performing a mutation (holder.double();) while the immutable reference (v of holder.var) is still in use. Just like you've mentioned,
This can't be allowed because the variant may have been changed and v may not even make sense as a value any more.
However, we, the programmer, have made the following rule: holder.double() can only modify v; all other fields should remain the same. E.g. holder.double() can do v.foo = 13, but cannot do self.var = TwoVariants::V2(...). If the rule is followed, there should be no problem to access v after calling holder.double(), since it is still the same v, only that the v.foo has changed.
Now the question is, how to access v after calling holder.double()?
An Unsafe Solution
As pointed out by L. Riemer in the comment, you can use raw pointers with unsafe constructs. Modify the match expression in main function to the following code and it should compile:
match &holder.var {
TwoVariants::V1(v) => {
// Create a pointer pointing to v.
let pv = v as *const NonCopyType;
holder.double();
// Dereference the pointer, then create a reference to v.
let v = unsafe { &*pv };
// Access v as usual.
println!("{}", v.foo);
},
_ => ()
}
Note that THIS METHOD IS STRONGLY DISCOURAGED, because the compiler cannot guarantee the validity of the data pointed by pv at compile time, and there is no runtime error detection too. We just assume that the v gotten from dereferencing pv is the original v and holder.double() will always follow the rule.
To illustrate the point, try to compile with the modified VariantHolder::double():
fn double(&mut self) {
match &mut self.var {
TwoVariants::V1(v) => {
v.foo *= 2;
// Assume that we accidentally perform some operations that modify
// self.var into TwoVariants::V2.
self.var = TwoVariants::V2(NonCopyType { foo: v.foo + 1 });
},
TwoVariants::V2(v) => {
v.foo *= 2;
}
}
}
We can see that it compiles fine. 3 will be printed if you run it, that means v is actually an element of TwoVariants::V2 after calling holder.double(), not the original v anymore.
This kind of bug that compiles fine and produces no runtime error is very hard to spot, pin down and fix. If you add in heap allocations and threads into the system, things will become much more complicated, who knows which operation will break the rule and invalidate pv.
A (safe) RefCell Solution
Recall that our rule only allows modification of v. One workaround is to utilize the Interior Mutability Pattern with std::cell::RefCell:
use std::cell::RefCell;
struct NonCopyType {
foo: u32
}
enum TwoVariants {
// Wrap NonCopyType in RefCell, since this is the start of modification
// point allowed by our *rule*.
V1(RefCell<NonCopyType>),
V2(RefCell<NonCopyType>)
}
struct VariantHolder {
var: TwoVariants
}
impl VariantHolder {
// Remove mut, telling the compiler that the `double()` function does not
// need an exclusive reference to self.
fn double(&self) {
match &self.var {
TwoVariants::V1(v) => {
// Borrow mutable from v and modify it.
v.borrow_mut().foo *= 2;
},
TwoVariants::V2(v) => {
v.borrow_mut().foo *= 2;
}
}
}
}
fn main() {
// Create a RefCell to contain NonCopyType.
let var = TwoVariants::V1(RefCell::new(NonCopyType {
foo: 1
}));
let mut holder = VariantHolder {
var
};
match &holder.var {
TwoVariants::V1(v) => {
// Now `double()` only borrow immutably from `holder`, fixing the
// "borrow as mutable while immutable reference is still alive"
// problem.
holder.double();
// Borrow from v.
let v = v.borrow();
// Access v as usual.
println!("{}", v.foo);
},
_ => ()
}
}
In essence we are telling the compiler about our rule, i.e. In the double() function, holder, var, and TwoVarients are immutable, only v is mutable.
The advantage of this approach over the unsafe one is compiler can help us to make sure our rule is followed. Accidental modification in double() such as self.var = TwoVariants::V2(...) will result in compile error. RefCell enforces the borrowing rule at runtime, which will panic! immediately if violation of the rule occurs.
Difference Between Using RefCell and If-Let Solutions
There are some subtle differences between the RefCell solution and if let solution. The if let solution may look something like this:
match &holder.var {
TwoVariants::V1(v) => {
holder.double();
// Use if-let to unpack and get v from holder.var.
if let TwoVariants::V1(v) = &holder.var {
// Access v as usual.
println!("{}", v.foo);
} else {
panic!("*Rule* violated. Check `holder.double()`.");
}
},
_ => ()
}
The checking of our rule is performed in runtime, instead of compile time like the RefCell solution.
If you do self.var = TwoVariants::V1(NonCopyType { ... }) inside VariantHolder::double(), the if let clause will still extract v out successfully. However, this extracted v is no longer the original v. This fact is important if the NonCopyType has more than 1 field.

Borrowing a structure which reference Strings - value borrowed here after partial move [duplicate]

I'm new to Rust. Below is my testing.
#[derive(Debug)]
enum Food {
Cake,
Pizza,
Salad,
}
#[derive(Debug)]
struct Bag {
food: Food
}
fn main() {
let bag = Bag { food: Food::Cake };
match bag.food {
Food::Cake => println!("I got cake"),
x => println!("I got {:?}", x)
}
println!("{:?}", bag);
}
When I run it, I got an error.
error[E0382]: borrow of moved value: `bag`
--> src\main.rs:20:22
|
17 | x => println!("I got {:?}", x)
| - value moved here
...
20 | println!("{:?}", bag);
| ^^^ value borrowed here after partial move
|
= note: move occurs because `bag.food` has type `Food`, which does not implement the `Copy` trait
It's clear that bag.food will not match x arm in the code. Why a move happens there?
It doesn't matter that the x branch is not taken at runtime because whether a match takes ownership of its argument does not depend on which branch of the match will actually be taken. Much like in this code (also see this followup question):
let foo = "blargh".to_owned();
if false {
let _bar = foo;
}
println!("{}", foo); // error[E0382]: borrow of moved value: `foo`
foo is never actually moved, but that doesn't make any difference to whether it is valid after the if (it isn't).
The match in the question takes ownership of bag.food (invalidating bag) because it has a branch that takes ownership. If you want that particular branch to not take ownership, you can use a ref pattern to borrow instead:
match bag.food {
Food::Cake => println!("I got cake"),
ref x => println!("I got {:?}", x)
}
Alternatively, since Rust 1.26 the compiler knows how to bind value patterns (such as Food::Cake) to references (such as &bag.food), so you can write:
match &bag.food {
Food::Cake => println!("I got cake"),
x => println!("I got {:?}", x)
}
In this case Food::Cake matches the value, but x matches and is bound to the reference, so it does the same thing as the previous snippet with ref. (You may see this referred to as "default binding modes" or "match ergonomics".)

Does non-matching arm take the owner of a variable in a "match" statement in Rust?

I'm new to Rust. Below is my testing.
#[derive(Debug)]
enum Food {
Cake,
Pizza,
Salad,
}
#[derive(Debug)]
struct Bag {
food: Food
}
fn main() {
let bag = Bag { food: Food::Cake };
match bag.food {
Food::Cake => println!("I got cake"),
x => println!("I got {:?}", x)
}
println!("{:?}", bag);
}
When I run it, I got an error.
error[E0382]: borrow of moved value: `bag`
--> src\main.rs:20:22
|
17 | x => println!("I got {:?}", x)
| - value moved here
...
20 | println!("{:?}", bag);
| ^^^ value borrowed here after partial move
|
= note: move occurs because `bag.food` has type `Food`, which does not implement the `Copy` trait
It's clear that bag.food will not match x arm in the code. Why a move happens there?
It doesn't matter that the x branch is not taken at runtime because whether a match takes ownership of its argument does not depend on which branch of the match will actually be taken. Much like in this code (also see this followup question):
let foo = "blargh".to_owned();
if false {
let _bar = foo;
}
println!("{}", foo); // error[E0382]: borrow of moved value: `foo`
foo is never actually moved, but that doesn't make any difference to whether it is valid after the if (it isn't).
The match in the question takes ownership of bag.food (invalidating bag) because it has a branch that takes ownership. If you want that particular branch to not take ownership, you can use a ref pattern to borrow instead:
match bag.food {
Food::Cake => println!("I got cake"),
ref x => println!("I got {:?}", x)
}
Alternatively, since Rust 1.26 the compiler knows how to bind value patterns (such as Food::Cake) to references (such as &bag.food), so you can write:
match &bag.food {
Food::Cake => println!("I got cake"),
x => println!("I got {:?}", x)
}
In this case Food::Cake matches the value, but x matches and is bound to the reference, so it does the same thing as the previous snippet with ref. (You may see this referred to as "default binding modes" or "match ergonomics".)

Why do I need to implement `Copy` and `Clone` if I'm sharing an immutable reference with multiple threads?

Still struggling with the Rust mind shift, now I have this use case - one configuration for a multi-threaded TcpListener:
use std::net::{TcpListener, TcpStream, ToSocketAddrs};
use std::thread;
fn main() {
serve("127.0.0.1:3333", Configuration { do_something: true });
}
//#[derive(Copy, Clone)]
pub struct Configuration {
pub do_something: bool,
}
pub fn serve<A: ToSocketAddrs>(addr: A, conf: Configuration) {
let listener = TcpListener::bind(addr).expect("bind failed");
for stream in listener.incoming() {
match stream {
Ok(stream) => {
thread::spawn(move || {
handle_client(stream, &conf);
});
}
Err(e) => {
println!("Connection failed: {}", e);
}
}
}
}
fn handle_client(stream: TcpStream, conf: &Configuration) {
if conf.do_something {
//stream....
}
}
I'm happy that the TcpStream is consumed by handle_client, that's it's purpose, but why does Configuration have to be copied for each thread? I'd like to have just one copy and share an immutable reference with all threads. Is that possible? Or perhaps I'm missing the point.
Why do I need the Copy and Clone traits if I'm passing Configuration by reference? This confused me a good deal:
error[E0382]: capture of moved value: `conf`
--> src/main.rs:19:64
|
19 | thread::spawn(move || { handle_client(stream, &conf); });
| ------- ^^^^ value captured here after move
| |
| value moved (into closure) here
|
= note: move occurs because `conf` has type `Configuration`, which does not implement the `Copy` trait
Implementing Copy (Clone is incidental here) only fixes the problem because implementing it allows the compiler to implicitly duplicate the Configuration struct, passing a copied value into the thread.
It needs to pass the variable by value because you told the compiler to move all used values into the closure:
thread::spawn(move || {
// ^^^^ HERE
handle_client(stream, &conf);
});
The entire purpose of the move keyword is to tell the compiler "no, don't try to infer how variables are used inside this closure, just move everything in".
When you move &conf, the compiler says "OK, I'll move conf into the closure then take a reference to it".
In your case, you can just remove the move keyword:
thread::spawn(|| {
handle_client(stream, &conf);
});
If you really need to be able to use the move keyword and pass in a reference, you need to move in a reference:
let conf = &conf;
thread::spawn(move || {
handle_client(stream, conf);
});
This still doesn't allow your code to compile because there's no guarantee that the reference outlives the thread. That's discussed thoroughly in Passing a reference to a stack variable to a scoped thread.

Resources