R: AsRef<[T]> should work for T=u64?

On the following:

pub fn a<
        T: Copy,
        R: AsRef<[T]> 
    >(
        a_fn: &dyn Fn(&[R], &mut [u64]),
    ) 
{
    let mut result = vec![0, 3];
    b(&[1,2,3], result.as_mut_slice());
}
    
    
fn b<T: Copy, R: AsRef<[T]>>(_: &[R], _: &mut [u64]) {
    unimplemented!();
}
    
fn c() {
    a::<u64, &[u64]>(&b);
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=43d054772b15fa575cc75e87b4eb34d9

I'm trying to be generic on the type R, but eventually use a slice of u64. However, it looks like that [integer] does not implement AsRef<[T]>.

Error:

   Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `{integer}: AsRef<[_]>` is not satisfied
  --> src/lib.rs:9:5
   |
9  |     b(&[1,2,3], result.as_mut_slice());
   |     ^ the trait `AsRef<[_]>` is not implemented for `{integer}`
   |
   = help: the following implementations were found:
             <&T as AsRef<U>>
             <&mut T as AsRef<U>>
             <Arc<T> as AsRef<T>>
             <Box<T, A> as AsRef<T>>
           and 38 others
note: required by a bound in `b`
  --> src/lib.rs:13:18
   |
13 | fn b<T: Copy, R: AsRef<[T]>>(_: &[R], _: &mut [u64]) {
   |                  ^^^^^^^^^^ required by this bound in `b`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error

The error occurs because no integer types implement AsRef<[T]> for any T. I suppose there could be an implementation that converts to a single element slice containing the integer, but that doesn't look to be implemented in the standard library since it probably isn't what people want when using AsRef. It seems more likely that you mean to say something like [R] : AsRef<[T]>, which does compile (playground). You may want something similar on a() as well.

But I'm setting R = &mut[u64] on my example. Why it gets to be u64?

In b I don't think you want R to be a slice of AsRef<[T]>, just a AsRef<[T]>. This seems to compile your example

pub fn a<
        T: Copy,
        R: AsRef<[T]> 
    >(
        a_fn: &dyn Fn(&[R], &mut [u64]),
    ) 
{
    let mut result = vec![0, 3];
    b(&[1,2,3], result.as_mut_slice());
}
    
    
fn b<T: Copy, R: AsRef<[T]> + ?Sized >(_: &R, _: &mut [u64]) {
    unimplemented!();
}
    
fn c() {
    a::<u64, &[u64]>(&b as _);
}

Alternatively this also works

pub fn a<
        'a,
        T: Copy,
        R: AsRef<[T]> 
    >(
        a_fn: &dyn Fn(&'a [R], &'a mut [u64]),
    ) 
{
    let mut result = vec![0, 3];
    b(&[1,2,3], result.as_mut_slice());
}
    
    
fn b<T: Copy, R: AsRef<[T]>>(_: R, _: &mut [u64]) {
    unimplemented!();
}
    
fn c() {
    a::<u64, &[u64]>(&b as _);
}
1 Like

It's not part of a trait like AsRef, but core::slice::from_ref (and from_mut) exist.


You have multiple generic instantiations going on, and it's unclear exactly what you expected. But let's take a look. In fn c() we have:

    a::<u64, &[u64]>(&b);

Which "sets" T = u64 and R = &[u64] in the call to a. u64 satisfies Copy and &[u64] satisfies AsRef[u64] (AsRef[T]), so that meets the bounds on a. It also means that the parameter to a is a &dyn Fn(&[R], &mut [u64]) :arrow_right: &dyn Fn(&[&[u64]], &mut [u64]).

I doubt this is what you wanted, but the bounds line up on fn b, and so you can coerce &b successfully, and this all compiles.

Next, in fn a, you're not actually using your a_fn parameter. And you're not using the A or R type parameters in your call to b. So this line:

    b(&[1,2,3], result.as_mut_slice());

Is completely independent of anything discussed above. The fact that you gave the type parameters the same name and bounds on fn a and fn b doesn't tie them together, any more than two functions that take input: String are tied together. This is a whole new instantiation, where we have to resolve the type parameters in this call to b.

(If I understand your question, this is why "it [that is, R] gets to be u64 [really, {integer}]".)

The bounds on b mean that result.as_mut_slice() must be a &mut [u64] -- no problems there. And it means that &[1, 2, 3] must be a &[R] where R: AsRef<[T]> for some T: Copy. Which must mean R is some {integer}. And so this is where the error about {integer}: AsRef<[_]> not being implemented is coming from.


If there was some fn as_slice_of_arrays<T>(slice: &[T]) -> &[[T; 1]], the call to b could be make to work without changing the slice:

    b(as_slice_of_arrays(&[1,2,3]), result.as_mut_slice());

There's not, but here's the "manual" version.

    // R: &[[i32; 1]], T: [i32: 1]
    b(&[[1],[2],[3]], result.as_mut_slice());

(But maybe @Cocalus is right and you want to change b instead. Or maybe you mean to use a_fn, I don't know.)

2 Likes

The answer for this is that the typechecking is performed for each function separately, using only the signatures of the other functions. So when Rust typechecks a() it can't see what the parameters of a() get set to in c(), it has to assume they can be anything. It also doesn't assume the R in the signature of b() is the same as the R in the signature of a(), so all it has to go on is that it is looking for b() to accept a &[R] where R : AsRef<T> for some T that is Copy. You have passed &[1,2,3], which is of type &[{integer}], where {integer} is some integer type that hasn't been inferred yet. Matching that against the parameter type, we get &[R] = &[{integer}], so R = {integer}, but no integer type implements AsRef<T> for any T so it fails. That probably isn't what you want R to be, and it might help if you explain what you're trying to do so we can help figure out what the types should be here.

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.