Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Destructuring an Option<Box<_>> inside a match statement in Rust

I have a big object that I need boxed in another object but I don't necessarily need it all the time. So I want to use an if statement to get the optional boxed TempStructure but i'm not exactly sure how I can destructure and dereference at the same time.

Example:

pub struct TempStructure {
    lots_of_data: [u64; 64],
}
pub struct Structure {
    pending_removal: Option<Box<(TempStructure, bool)>>,
}
impl Structure {
    pub fn do_somthing(&mut self) {
        // How do I destructure the Option and dereference the Box to get TempStructure?
        if let Some((temp_structure, some_boolean)) = self.pending_removal.take() { 
            // Do something with temp_structure and some_boolean
        }
    }
}

When I do this ^^^ I get an expected struct `std::boxed::Box`, found tuple error.

like image 815
Zyansheep Avatar asked Mar 15 '21 12:03

Zyansheep


People also ask

What is Destructure in Rust?

Destructuring is the process of breaking down items into their component parts, binding each to smaller variables.

What is a match statement Rust?

Rust provides pattern matching via the match keyword, which can be used like a C switch . The first matching arm is evaluated and all possible values must be covered.

Does Rust have pattern matching?

Patterns are a special syntax in Rust for matching against the structure of types, both complex and simple. Using patterns in conjunction with match expressions and other constructs gives you more control over a program's control flow.

How do you match types in Rust?

Instead of matching on an element, call the method in the trait on it. TLDR: in Rust, to match over type, we create a trait, implement a function for each type and call it on the element to match. Surround it with backticks to mark it as code. Single backticks for inline code, triple backticks for code blocks.


2 Answers

Dereference the box after matching:

if let Some(inner) = self.pending_removal.take() {
    let (temp_structure, some_boolean) = *inner;
    // Do something with temp_structure and some_boolean
}

(playground)

If you think this is a bit clunky, you're right. On nightly you can use the unstable box_patterns feature to enable a better syntax for this (although this might never be stabilized):

if let Some(box (temp_structure, some_boolean)) = self.pending_removal.take() {
    // Do something with temp_structure and some_boolean
}

(playground)

like image 112
Smitop Avatar answered Oct 16 '22 23:10

Smitop


You can fix this with adding a .as_deref() after the take():

pub struct TempStructure {
    lots_of_data: [u64; 64],
}
pub struct Structure {
    pending_removal: Option<Box<(TempStructure, bool)>>,
}
impl Structure {
    pub fn do_somthing(&mut self) {
        // How do I destructure the Option and dereference the Box to get TempStructure?
        if let Some((temp_structure, some_boolean)) = self.pending_removal.take().as_deref() { 
            // Do something with temp_structure and some_boolean
        }
    }
}

Box<T> dereferences to &T, as_deref() dereferences the value of the Option, therefore it gives you a &T out of your Option<Box<T>>.

Edit: another option is to dereference the Box to move the value out of it:

        if let Some((temp_structure, some_boolean)) = self.pending_removal.take().map(|boxed| *boxed) { 
            // assignments just for demonstration purposes that we now get
            // owned values rather than references.
            let _: TempStructure = temp_structure;
            let _: bool = some_boolean;
        }
like image 36
sebpuetz Avatar answered Oct 16 '22 23:10

sebpuetz