Return Option of reference and use it in Match

Brief code


#[derive(Debug)]
struct Param {
    p1: u32,
    p2: u32,
}

#[derive(Debug)]
struct Res {
    r1: u32,
    r2: u32,
}

#[derive(Debug)]
struct SearchCall {
    param: Param,
    result: Res,
}

impl SearchCall {
    pub fn new(n: u32) -> SearchCall {
        SearchCall {
            param: Param { p1: n, p2: n },
            result: Res { r1: n, r2: n },
        }
    }
}

struct Context {
    search_list: Vec<SearchCall>,
}

impl Context {
    pub fn new() -> Context {
        Context {
            search_list: Vec::with_capacity(2),
        }
    }

    fn find_search(&self, n: u32) -> Option<&SearchCall> {
        for s in &self.search_list {
            if s.param.p1 == n {
                return Some(&s);
            }
        }
        return None;
    }

    pub fn add(&mut self, n: u32) {
        let s = SearchCall::new(n);
        self.search_list.push(s);
    }

    pub fn search(&mut self, n: u32) -> Res {
        let o = self.find_search(n);
        match o {
            None => Res { r1: 0, r2: 0, },
            Some(&s) => Res { r1: s.result.r1, r2: s.result.r2 },
        }
    }
}

fn main() {
    let mut ctx = Context::new();
    ctx.add(1);
    ctx.add(3);
    let r = ctx.search(1);
    println!("res {:?}", r)
}

error:

error[E0507]: cannot move out of `*o` as enum variant `Some` which is behind a shared reference
  --> src/main.rs:73:15
   |
73 |         match o {
   |               ^
74 |             None => Res { r1: 0, r2: 0, },
75 |             Some(&s) => Res { r1: s.result.r1, r2: s.result.r2 },
   |                   -
   |                   |
   |                   data moved here
   |                   move occurs because `s` has type `SearchCall`, which does not implement the `Copy` trait


How I can return Option<&T> and then use Match statement to getting &T fields?

match on Option.as_ref

1 Like

The compiler's suggestion also works, but better yet, just do

        match o {
            None => Res { r1: 0, r2: 0, },
            Some(s) => Res { r1: s.result.r1, r2: s.result.r2 },
        }

The & in your pattern was "stripping away" the reference, and trying to move from underneath it. I recommend reading this article to understand more.


Incidentally you can write find_search like so:

    fn find_search(&self, n: u32) -> Option<&SearchCall> {
        self.search_list.iter().find(|s| s.param.p1 == n)
    }

And though more a matter of style preference, you could also write

    pub fn search(&mut self, n: u32) -> Res {
        self.find_search(n)
            .map(|s| Res { r1: s.result.r1, r2: s.result.r2 })
            .unwrap_or(Res { r1: 0, r2: 0, })
    }

And looking at that, you may also want to consider

-#[derive(Debug)]
+#[derive(Debug, Clone, Default)]
+// Maybe Copy too...
struct Res {
    r1: u32,
    r2: u32,
}

So you can

    pub fn search(&mut self, n: u32) -> Res {
        self.find_search(n)
            .map(|s| s.result.clone())
            .unwrap_or_default()
    }

Playground.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.