Dropping 2 levels of match, dropping as_ref

  1. Consider the following code:
struct Cat {
    age: i32,
    name: String,
}

#[test]
pub fn test_000() {
    let v: Vec<Rc<Cat>> = vec![];
    match v.as_slice() {
        [x, y] => match (x.as_ref(), y.as_ref()) {
            (
                Cat { age, name },
                Cat {
                    age: age1,
                    name: name1,
                },
            ) => {}
        },
        _ => {} // [Cat { age, name }, y] => {}
    }
}

What I don’t like is that due to the Rc, I need to have 2 levels of “match”, and call “as_ref”. Is there anyway to solve this problem (without changing the type of v) to only use one level of match?

1 Like

As a followup, I am trying:


trait SliceRefT<'a, T> {
    fn slice_ref(v: Self) -> Vec<&'a T>;
}

impl<'a, T> SliceRefT<'a, T> for Vec<Rc<T>> {
    fn slice_ref(v: Self) -> Vec<&'a T> {
        let mut ans = vec![];
        for x in v.iter() {
            ans.push(x.as_ref())
        }
        ans
    }
}

but I guess this can not work as we can not return a ref to a Rc<…>.as_ref()

https://www.reddit.com/r/rust/comments/7qbpod/dereferencing_rc_in_a_pattern/ seems to suggest this problem can not be solved

You can use .iter().map(Rc::as_ref).take(N).collect() to match on (up to) the first N elements of slice of Rcs. This does not get rid of as_ref as you wished, since, as you noticed, it is not possible, but it does reduce rightward drift.

And to avoid an unneeded heap allocation, you can use an ArrayVec:

use ::std::{
    cell::Cell,
    rc::Rc,
    slice,
};
use ::arrayvec::ArrayVec; // 0.4.10

#[derive(Debug)]
pub
struct Cat {
    name: String,
    age: u8,
}

fn observe (cats: &[Rc<Cat>])    
{
    print!("Observing... ");
    const N: usize = 2; // maximum number of elements matched
    match cats
            .iter()
            .map(Rc::as_ref)
            // .take(N).collect::<Vec<_>>()[..]
            .collect::<ArrayVec<[_; N]>>()[..]
    {
        // 2 (or more) Cats
        | [
            &Cat {
                name: ref name1,
                age : ref age1,
            },
            &Cat {
                name: ref name2,
                age : ref age2,
            },
        ] => {
            println!(
                concat!(
                    "First 2 Cats: ",
                    "{} ({} years old) and {} ({} years old). ",
                    "Remaining Cats: {:?}",
                ),
                name1, age1,
                name2, age2,
                &cats[2 ..],
            );
        },

        // 1 Cat
        | [
            &Cat { ref name, ref age },
        ] => {
            println!(
                "Just the one Cat: {} ({} years old).",
                name, age,
            );
        },
        
        // 0 Cats
        | _ => {
            println!("No cat was found (alive).")
        }
    }
}
5 Likes