A problem about `AsRef `

I have these codes:

struct DataSave {
    vec_f32: Vec<f32>,
    vec_vec_f32: Vec<Vec<f32>>,
}

trait TwoStepsCalc {
    type FirstStepOutput<'a>;
    type SecondStepEleOutput;
    fn calc_first_step<'a>(&self, data_save: &'a DataSave) -> Self::FirstStepOutput<'a>;
    fn calc_second_step<T, N>(&self, data: T) -> Vec<Self::SecondStepEleOutput>
    where 
        T: AsRef<[N]>,
        for<'a> Self::FirstStepOutput<'a>: AsRef<[N]>
    ; 
}

struct CalcVec;

impl TwoStepsCalc for CalcVec {
    type FirstStepOutput<'a> = Vec<f32>;
    type SecondStepEleOutput = f32;
    fn calc_first_step<'a>(&self, data_save: &'a DataSave) -> Self::FirstStepOutput<'a> {
        data_save.vec_f32.to_vec()
    }
    fn calc_second_step<T, N>(&self, data: T) -> Vec<Self::SecondStepEleOutput>
    where 
        T: AsRef<[N]>,
        for<'a> Self::FirstStepOutput<'a>: AsRef<[N]>
    {
        data.as_ref().iter().map(|x| *x + 1f32).collect()
    }
}

and the compile error:

[E0369] Error: cannot add `f32` to `N`
    โ•ญโ”€[command_5:1:1]
    โ”‚
 12 โ”‚         for<'a> Self::FirstStepOutput<'a>: AsRef<[N]>
    ยท                                                      โ”‚ 
    ยท                                                      โ•ฐโ”€ help: consider further restricting type parameter `N`: `, N: std::ops::Add<f32>`
    ยท 
 14 โ”‚         data.as_ref().iter().map(|x| *x + 1f32).collect()
    ยท                                      โ”€โ”ฌ โ”ฌ โ”€โ”€โ”ฌโ”€  
    ยท                                       โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ N
    ยท                                         โ”‚   โ”‚   
    ยท                                         โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€ error: cannot add `f32` to `N`
    ยท                                             โ”‚   
    ยท                                             โ•ฐโ”€โ”€โ”€ f32
โ”€โ”€โ”€โ”€โ•ฏ

In my understanding, when I have this constraint: for<'a> Self::FirstStepOutput<'a>: AsRef<[N]> in the implementation of TwoStepsCalc for CalcVec, because FirstStepOutput is a AsRef<[f32]>, so the compiler should know that N is f32. But it seems not. What am I doing wrong?

AsRef could be implemented with many different slice types for any given type. In Vec's case it's true that as things currently stand it wouldn't be possible to add an implementation that didn't violate the coherence rules, but the compiler doesn't use the coherence rules to prove that a type parameter must be equivalent to a concrete type like that.

It's hard to say what the best fix is here without more context, but one option is to move N to be a type parameter on the trait instead. Then you can either only implement the trait with f32, or specify the additional bounds that allow you to work with N.

Playground

use std::ops::Add;

struct DataSave {
    vec_f32: Vec<f32>,
    #[allow(dead_code)]
    vec_vec_f32: Vec<Vec<f32>>,
}

trait TwoStepsCalc<N> {
    type FirstStepOutput<'a>;
    type SecondStepEleOutput;
    fn calc_first_step<'a>(&self, data_save: &'a DataSave) -> Self::FirstStepOutput<'a>;
    fn calc_second_step<T>(&self, data: T) -> Vec<Self::SecondStepEleOutput>
    where
        T: AsRef<[N]>,
        for<'a> Self::FirstStepOutput<'a>: AsRef<[N]>;
}

struct CalcVec;

impl TwoStepsCalc<f32> for CalcVec {
    type FirstStepOutput<'a> = Vec<f32>;
    type SecondStepEleOutput = f32;
    fn calc_first_step<'a>(&self, data_save: &'a DataSave) -> Self::FirstStepOutput<'a> {
        data_save.vec_f32.to_vec()
    }
    fn calc_second_step<T>(&self, data: T) -> Vec<Self::SecondStepEleOutput>
    where
        T: AsRef<[f32]>,
        for<'a> Self::FirstStepOutput<'a>: AsRef<[f32]>,
    {
        data.as_ref().iter().map(|x| x + 1f32).collect()
    }
}

struct GenericCalcVec;

impl<N> TwoStepsCalc<N> for GenericCalcVec
where
    for<'a> &'a N: Add<f32, Output = f32>,
{
    type FirstStepOutput<'a> = Vec<f32>;
    type SecondStepEleOutput = f32;
    fn calc_first_step<'a>(&self, data_save: &'a DataSave) -> Self::FirstStepOutput<'a> {
        data_save.vec_f32.to_vec()
    }
    fn calc_second_step<T>(&self, data: T) -> Vec<Self::SecondStepEleOutput>
    where
        T: AsRef<[N]>,
        for<'a> Self::FirstStepOutput<'a>: AsRef<[N]>,
    {
        data.as_ref().iter().map(|x| x + 1f32).collect()
    }
}
2 Likes

Thanks very much.
One more asking, how can I make a implementation of TwoStepsCalc as following:

struct DataSave {
    vec_f32: Vec<f32>,
    vec_f32_another: Vec<f32>,
    vec_vec_f32: Vec<Vec<f32>>,
}

struct CalcVecVec;

impl TwoStepsCalc<&'a Vec<f32>> for CalcVecVec {
    type FirstStepOutput<'a> = Vec<&'a Vec<f32>>;
    type SecondStepEleOutput = f32;
    fn calc_first_step<'a>(&self, data_save: &'a DataSave) -> Self::FirstStepOutput<'a> {
        vec![&data_save.vec_f32, &data_save.vec_f32_another]
    }
    fn calc_second_step<T>(&self, data: T) -> Vec<Self::SecondStepEleOutput>
    where 
        T: AsRef<[&'a Vec<f32>]>,
        for<'a> Self::FirstStepOutput<'a>: AsRef<[&'a Vec<f32>]>
    {
        let data_as_ref = data.as_ref();
        data_as_ref[0].iter().zip(data_as_ref[1].iter()).map(|(x, y)| x + y).collect()
    }
}

It is just a matter of lifetime anotation or I totally can not do it?

It would be a non-semver-breaking change for the standard library to add

impl AsRef<[()]> for Vec<f32>

(One of the balancing acts of coherence and generics is to give both upstream and downstream enough flexibility to make changes without breaking everything.)

More generally speaking, if you need a type parameter like N to have any particular capabilities in your function body, they have to be expressed as trait bounds.

1 Like

You can't do it without deeper changes. For calc_first_step to make sense, you need

impl<'y> TwoStepsCalc<&'y Vec<f32>> for CalcVecVec {
    type FirstStepOutput<'x> = Vec<&'x Vec<f32>>;
    type SecondStepEleOutput = f32;
    fn calc_first_step<'a>(&self, data_save: &'a DataSave) -> Self::FirstStepOutput<'a> {
        vec![&data_save.vec_f32, &data_save.vec_f32]
    }
    fn calc_second_step<T>(&self, data: T) -> Vec<Self::SecondStepEleOutput>
    where 
        T: AsRef<[&'y Vec<f32>]>,
        for<'a> Self::FirstStepOutput<'a>: AsRef<[&'y Vec<f32>]>,
    {
        let data_as_ref = data.as_ref();
        data_as_ref[0].iter().zip(data_as_ref[1].iter()).map(|(x, y)| x + y).collect()
    }
}

That compiles, but the higher-ranked trait bound on calc_second_step isn't actually tested until you try to use it, and then it fails because Vec<&'x T> implements AsRef<[&'x T]> not AsRef<[&'some_other_lifetime T]>.

1 Like

Here's something that runs anyway. N is no longer generic relative to the implementor. I don't know if it will work for you or not.

1 Like

Thanks very much. It works exactly as what I am needing.
I made the following tests:

  1. when type A = Vec<f32>, parameter data can be &Vec<f32>, Vec<f32>, &[f32]
  2. when type A = Vec<&[f32]>, parameter data can be Vec<&[f32]>, &Vec<&[f32]>,
  3. when type A = Vec<&Vec<f32>>, parameter data can be Vec<&Vec<f32>>, &Vec<&Vec<f32>>,

The limits is :

  1. when type A = Vec<&[f32]>, data can not be Vec<&Vec<f32>>,
  2. when type A = Vec<&Vec<f32>>, data can not be Vec<&[f32]>.

I think it is because that one type implemented on AsRef<[&Vec<f32>]>, the compiler can not make sure it also implemented on As<[&[f32]]>. But one type that is Vec<f32>, the compiler can make sure it is also a &[f32].

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.