rust-ndarray / ndarray

ndarray: an N-dimensional array with array views, multidimensional slicing, and efficient operations
https://docs.rs/ndarray/
Apache License 2.0
3.58k stars 304 forks source link

Use references for scalar operands #1200

Open zackangelo opened 2 years ago

zackangelo commented 2 years ago

It would be useful for me to be able to use references to scalars instead of owned instances when performing operations on arrays.

e.g., this fails:

let arr1 = array!([1, 2, 3]);
let four: i32 = 4;
let arr2 = arr1 + &four; //error

I naively tried modifying the ops macros to accept references but kept getting a conflicting implementation error. It seems that the compiler is worried that someone will come along an add an implementation to StaticOperand for reference types:

    = note: downstream crates may implement trait `impl_ops::ScalarOperand` for type `&_`

...which is strange because there's a 'static type bound on ScalarOperand?

In any case, if anyone has any guidance on how to proceed, I'd greatly appreciate it! I'm happy to open a PR with a nudge in the right direction.

jturner314 commented 2 years ago

I assume that you tried to add something like the following:

impl<'a, A, S, D, B> $trt<&'a B> for ArrayBase<S, D>
where
    A: Clone + $trt<&'a B, Output = A>,
    S: DataOwned<Elem = A> + DataMut,
    D: Dimension,
    B: ScalarOperand,
{
    ...
}

The problem is that the following implementation already exists:

impl<A, S, D, B> $trt<B> for ArrayBase<S, D>
where
    A: Clone + $trt<B, Output = A>,
    S: DataOwned<Elem = A> + DataMut,
    D: Dimension,
    B: ScalarOperand,
{
    ...
}

So, a downstream crate could do the following:

#[derive(Clone)]
struct Struct {}

impl ndarray::ScalarOperand for Struct {}
impl ndarray::ScalarOperand for &'static Struct {}

which would result in conflicting implementations of Add<&'static Struct> for ArrayBase<S, D>, Mul<&'static Struct> for ArrayBase<S, D>, etc.

It looks like you already found the solution in #1201 that I'd suggest -- remove the 'static bound from ScalarOperand and implement ScalarOperand for references. You can implement ScalarOperand for &i32, &f32, etc., or for &T where T: ScalarOperand.