How capture ref to a value in a "global" collection in a closure

The closure in the following code creates an instance of Series. This prevented me from creating a reference and storing it in the iters: Vec<_>. I was getting the "reference to temporary value" error.

To extend the lifetime of the value, I created a local_global_series: Vec<Series> outside of the closure. I was pretty confident that this solution would do the trick. But the compiler came back with:

captured variable cannot escape `FnMut` closure body
`FnMut` closures only have access to their captured variables while they are executing...
...therefore, they cannot allow references to capture variables to escape
pub fn build_logit(mut self) -> Result<Matrix<LogitData>> {

let iters: DataFrame = self.select(predictors)?;

let mut local_global_series: Vec<Series> = Vec::with_capacity(rows);

let mut iters = iters  // issue is not caused by mutability here
    .iter()
    .map(|s| {                        // <<< inferred FnMut closure
        local_global_series.push(     // <<< variable captured here
            s.cast(&DataType::Float64)
                .expect("Tried to cast to Float64 failed"),
        );
        let r = local_global_series.last().unwrap();
        let result = r.f64()?.into_iter();  // uses ref to local_global, 
                                            // returns something that borrows/relies 
                                            // on the value

        Ok(result)                    // <<< cannot escape error here
    })
    .collect::<Result<Vec<_>>>()?;  

larger context
impl Matrix<DataFrame> {
    fn new(inner: DataFrame) -> Self {
        Matrix { inner }
    }
    pub fn build_logit(mut self) -> Result<Matrix<LogitData>> {
        self.reshape_to_logit()?;

        let iters: DataFrame = self.select(predictors)?;

        let mut local_global_series: Vec<Series> = Vec::with_capacity(rows);

        let mut iters = iters
            .iter()
            .map(|s| {                // <<< inferred FnMut closure
                local_global_series.push(   // <<< variable captured here
                    s.cast(&DataType::Float64)
                        .expect("Tried to cast to Float64 failed"),
                );
                let r = local_global_series.last().unwrap();
                let result = r.f64()?.into_iter(); // uses ref to local_global,
                                                   // returns something that borrows/relies
                                                   // on the value

                Ok(result)                  // <<< cannot escape error here
            })
            .collect::<Result<Vec<_>>>()?;

        for _ in 0..self.height() {
            for iter in &mut iters {
                let value = iter.next().expect("should have as many iterations as rows");
                data.push(value.unwrap() as f64);
            }
        }

        // true means row major layout of array
        let logit_matrix = Matrix::<LogitData>::new(DenseMatrix::new(rows, cols, data, true), y);

        Ok(logit_matrix)
    }
}

Q1

Just to level-set, it would be great to hear again how/if its possible to invalidate the references to a mutable object

Q2

Is there another workaround to this problem?

Please provide a repro. It's unclear where exactly the error is, and there are several unknowns wrt. external dependencies (eg. what self is, where the type DataFrame comes from).

I believe this is what you're seeing:

    let mut v = vec!["".to_string()];
    let _lambda = |s: &str| { v.push(s.to_string()); v.last().unwrap() };

error: captured variable cannot escape `FnMut` closure body
 --> src/main.rs:3:54
  |
2 |     let mut v = vec!["".to_string()];
  |         ----- variable defined here
3 |     let _lambda = |s: &str| { v.push(s.to_string()); v.last().unwrap() };
  |                           -   -                      ^^^^^^^^^^^^^^^^^ returns a reference to a captured variable which escapes the closure body
  |                           |   |
  |                           |   variable captured here
  |                           inferred to be a `FnMut` closure
  |
  = note: `FnMut` closures only have access to their captured variables while they are executing...
  = note: ...therefore, they cannot allow references to captured variables to escape

And the issue is that the FnMut trait looks like so:

fn call_mut(&mut self, args: Args) -> Self::Output;
// Your closure  ^^^^    Return value ^^^^^^^^^^^^

Which cannot support borrowing from the closure itself. That would require a different set of Fn traits that used GAT or similar:

fn call_borrowing_mut(&mut self, args: Args) -> Self::Output<'_>;
// The return value borrows from `&mut self`    ^^^^^^^^^^^^^^^^

Another reason it can't work is you're trying to hold on to references to the inside of the Vec while still pushing into it. Rust will throw a borrow error about that.


To workaround, try collecting local_global_series first, then iterating that.

let local_global_series: Vec<Series> = iters
    .iter()
    .map(|s| { 
        s.cast(&DataType::Float64).expect("Tried to cast to Float64 failed");
    })
    .collect();

let mut iters = local_global_series
    .iter()
    .map(|s| Ok(r.f64()?.into_iter()))
    .collect::<Result<Vec<_>>>()?;

(Untested.)

2 Likes

Thank you for the response. Several good points.

The point you make here seems to be a "clear and present" violation. It took me by surprise based on posts made a few years back talking about the potential benefits of something like fn insert(&mut self, value: T) -> &T for Vec.

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.