Explanation of Restricted Borrowing

⚓ Rust    📅 2026-02-03    👤 surdeus    👁️ 8      

surdeus

The following code:

#[derive(Debug, thiserror::Error)]
enum Error<'p, 'v> {
    #[error("Remaining parameters are invalid: {remaining:?}")]
    InvalidRemaining { remaining: &'p str },

    #[error("Parameter name is too long: {parameter:?}")]
    NameTooLong { parameter: &'p str },
}

fn read_cmdline_while<const N: usize>(
    cmdline: &mut Peekable<Chars>,
    buffer: &mut heapless::String<N>,
    mut predicate: impl FnMut(char) -> bool,
) -> Result<usize, CapacityError> {
    buffer.clear();

    while let Some(c) = cmdline.next_if(|c| predicate(*c)) {
        buffer.push(c)?;
    }

    Ok(buffer.len())
}

fn read_parameter_name<'p, 'v>(
    cmdline: &mut Peekable<Chars>,
    name_buffer: &'p mut heapless::String::<128>,
) -> Result<(), Error<'p, 'v>> {
    read_cmdline_while(cmdline, name_buffer, |c| c.is_alphanumeric() || c == '_')
        .map_err(|_| Error::NameTooLong {
            parameter: name_buffer.as_str(),
        })
        .and_then(|char_count_read| {
            // Check if there were no valid parameter name chars.
            if char_count_read > 0 || cmdline.peek().is_none() {
                Ok(())
            } else {
                read_cmdline_while(cmdline, name_buffer, |_| true).ok();

                Err(Error::InvalidRemaining {
                    remaining: name_buffer.as_str(),
                })
            }
        })
}

results in the following error:

error[E0500]: closure requires unique access to `*name_buffer` but it is already borrowed
   --> src/params.rs:147:19
    |
139 |   fn read_parameter_name<'p, 'v>(
    |                          -- lifetime `'p` defined here
...
143 | /     read_cmdline_while(cmdline, name_buffer, |c| c.is_alphanumeric() || c == '_')
144 | |         .map_err(|_| Error::NameTooLong {
    | |                  --- borrow occurs here
145 | |             parameter: name_buffer.as_str(),
    | |                        ----------- first borrow occurs due to use of `*name_buffer` in closure
146 | |         })
147 | |         .and_then(|char_count_read| {
    | |                   ^^^^^^^^^^^^^^^^^ closure construction occurs here
...   |
152 | |                 read_cmdline_while(cmdline, name_buffer, |_| true).ok();
    | |                                             ----------- second borrow occurs due to use of `*name_buffer` in closure
...   |
158 | |         })
    | |__________- returning this value requires that `*name_buffer` is borrowed for `'p`

I do not understand how this borrowing failure is occurring. In no circumstance will name_buffer be borrowed in the .and_then closure, as it is only borrowed in the case of the call to Result::<(), heapless::CapacityError>::map_err—in which case, the .and_then closure is never called.

If anyone could elucidate the failure in my understanding—and potentially explain a solution to the above issue—I would be very grateful.

6 posts - 4 participants

Read full topic

🏷️ Rust_feed