Converting &[T] -> &[&T]

I need a function which processes a bunch of Xs:

fn process_vals(sample: &[ X]) { ... }
fn process_refs(sample: &[&X]) { ... }

Either the refs or the vals version will do, but I'd like there to be only one. I would guess that the refs version would be the one to go with. However, if I find myself holding a &[X] or a Vec<X>, what would be a good way of turning the inner Xs into &Xs?

The following works, but is a bit long-winded

fn slice_of_vals(xs: &[X]) { process_refs(&xs.iter().collect::<Vec<_>>()) }

Can I do better? Is there something along the lines of as_ref() that might be appropriate here?

Generic vs. specific conundrum

In the process of playing around with this, I found that the by-value version appears to work very directly in generic code, but not on code that uses a specific type:

// ----- Generic-by-val version -------------------------
fn process_vals_generic<T>(sample: &[T]) {}

fn foo_generic_vals<T>() {
    // A variety of ways of holding a bunch of Ts
    // All of these can be passed easily to process_vals_generic
    fn slice_of_refs<T>(ts:   &[&T]) { process_vals_generic( ts) }
    fn slice_of_vals<T>(ts:   &[ T]) { process_vals_generic( ts) }
    fn   vec_of_refs<T>(ts: Vec<&T>) { process_vals_generic(&ts) }
    fn   vec_of_vals<T>(ts: Vec< T>) { process_vals_generic(&ts) }
    // Two ways of holding a single T
    // Both easily passed to process_vals_generic
    fn just_one_ref<T>(t: &T) { process_vals_generic(&[ t]); }
    fn just_one_val<T>(t:  T) { process_vals_generic(&[&t]); }
}

// ----- Specific-by-val version ------------------------
struct X;

fn process_vals_specific(sample: &[X]) {}

fn foo_specific_vals() {
    // A variety of ways of holding a bunch of Xs
    // Unlike the generic version above, some of these fail to compile
    fn slice_of_refs(xs:   &[&X]) { process_vals_specific( xs) } // ERROR:  expected &[X], found &[&X]
    fn slice_of_vals(xs:   &[ X]) { process_vals_specific( xs) }
    fn   vec_of_refs(xs: Vec<&X>) { process_vals_specific(&xs) } // ERROR:  expected &[X], found &Vec<&X>
    fn   vec_of_vals(xs: Vec< X>) { process_vals_specific(&xs) }
    // Two ways of holding a single X
    // Unlike the generic version above, the first case fails to compile
    fn just_one_ref(x: &X) { process_vals_specific(&[ x]); } // ERROR:  expected X,  found &X
    fn just_one_val(x:  X) { process_vals_specific(&[ x]); }
}

playground

I suspect that the T being inferred by the compiler is not what I think this is.

How can this difference between the generic and non-generic code be explained?

You cannot convert &[T] to &[&T] in any other way than going through a Vec. However, you could use generics to accept either type with the same function:

fn process<T>(sample: &[T])
where
    T: Borrow<X>,
{
}

This accepts any type where &T can be converted to &X. Obviously this is the case for T = X, but it is also the case for T = &X.

11 Likes

[T] and [&T] have a different representation in memory, so there's no way to just cast a pointer from one to another. The references have to be computed and put somewhere first.

2 Likes

If your function operates over T: Trait, then consider implementing impl<T: Trait> Trait for &T { ... } that forwards methods.

For example of such pattern, Iterator is implemented for &mut I: Iterator in std::iter - Rust.

1 Like

Do you need to process them in an array?

If you just need sequential access to them, you could consider taking impl IntoIterator<Item = &X> instead. Since you can pass &[T] to that directly, and if you have &[&T] to can pass .iter().copied().

7 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.