Ndarray broadcast lifetime

The ArrayBase::broadcast method has the signature

pub fn broadcast<E>(&self, dim: E) -> Option<ArrayView<'_, A, E::Dim>>
where
    E: IntoDimension,
    S: Data, 

Why does the API tie the lifetime of the view to self? Is the prototype given by elision correct?That is, something like aview1(x).into_shape((4, 5)).unwrap().broadcast((3, 4, 5)).unwrap() isn't allowed by the borrow checker. I want the lifetime in the broadcast to be just constrained by the original x. Does the result require the self bound?

The underlying array data is shared - that is, broadcast() does not copy the underlying data. So the result of a broadcast has to be lifetime-entangled to the original array.

And the code you showed compiles OK for me:

fn main() {
    use ndarray::aview1;
    let init = [0i32; 20];
    let _ = aview1(&init)
        .into_shape((4, 5))
        .unwrap()
        .broadcast((3, 4, 5))
        .unwrap();
}

It only compiles because you drop both the temporary created by into_shape and the result of broadcast at the same time.

If you attempt to save the value and use it later, you get a complaint that the temporary created by into_shape does not live long enough (Rust Playground):

use ndarray::aview1;

fn main() {
    let x = [0i32; 20];

    let n = aview1(&x)
        .into_shape((4, 5))
        .unwrap()
        .broadcast((3, 4, 5))
        .unwrap();

    dbg!(n);
}

complains:

   Compiling playground v0.0.1 (/playground)
error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:5:13
   |
5  |       let n = aview1(&x)
   |  _____________^
6  | |         .into_shape((4, 5))
7  | |         .unwrap()
   | |_________________^ creates a temporary value which is freed while still in use
8  |           .broadcast((3, 4, 5))
9  |           .unwrap();
   |                    - temporary value is freed at the end of this statement
10 |           
11 |       dbg!(n);
   |            - borrow later used here
   |
help: consider using a `let` binding to create a longer lived value
   |
5  ~     let binding = aview1(&x)
6  +         .into_shape((4, 5))
7  +         .unwrap();
8  ~     let n = binding
   |

For more information about this error, try `rustc --explain E0716`.
error: could not compile `playground` (bin "playground") due to previous error

I'm sorry my question wasn't clear. There are two lifetimes at play: the lifetime of x and the lifetime of the borrowed ArrayView. I want the lifetime of the broadcast to be tied to x because I don't want to have to name the view.

(x doesn't have a lifetime.)

The lifetime of &x is part of a generic type from the point of view of ArrayBase::broadcast:

    let x = [0i32; 20];

    // pub fn aview1<A>(xs: &[A]) -> ArrayView1<'_, A>
    // alias => ArrayView<'a, A, Ix1>
    // alias => ArrayBase<ViewRepr<&'a A>, D>
    // That's `ArrayBase<S, D>` with `S = ViewRepr<&'a A>`
    let n = aview1(&x)
        // pub fn into_shape<E>(self, shape: E) -> Result<ArrayBase<S, E::Dim>, ShapeError> 
        .into_shape((4, 5))
        .unwrap();
    
    // Now you have an `ArrayBase<S, X>` with `S = ViewRepr<&'a A>`
    // The `'a` and `A` aren't "visible" to `ArrayBase<S, X>`
    let n = n
        // pub fn broadcast<E>(&self, dim: E) -> Option<ArrayView<'_, X, E::Dim>> 
        .broadcast((3, 4, 5))
        .unwrap();

So the closest you could get to a signature that works is something like...

fn example<'inner, E>(&self, dim: E) -> Option<ArrayView<'inner, X, E::Dim>>
   where S: 'inner

But that is only sound if you know a lot about S and know you're basically doing a reborrow of some reference or similar. If S is a 'static type for example, that signature is unsound.

So practically speaking, there's probably no way to get the inner lifetime into the result of broadcast.

1 Like

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.