Open
Description
Code
struct Command(Vec<String>);
impl Command {
fn find_subcommand_mut(&mut self, _name: &str) -> Option<&mut Self> {
None
}
}
fn foo(app: &mut Command) {
let alias_definition = vec!["submodule", "status"];
let mut cmd = app;
for arg in alias_definition {
if let Some(next) = cmd.find_subcommand_mut(arg) {
cmd = next;
} else {
break;
}
}
dbg!(&cmd.0);
}
Current output
error[E0502]: cannot borrow `cmd.0` as immutable because it is also borrowed as mutable
--> src/lib.rs:18:10
|
12 | if let Some(next) = cmd.find_subcommand_mut(arg) {
| --- mutable borrow occurs here
...
18 | dbg!(&cmd.0);
| ^^^^^^
| |
| immutable borrow occurs here
| mutable borrow later used here
Desired output
"use -Zpolonius lol"
Rationale and extra context
"immutable borrow used here" pointing at the same span as "mutable borrow used here" is very unhelpful. the only case this could possibly make sense is when the code is in a loop, which this is not.
i originally ran into this problem inside of an (unrelated) loop, making the diagnostic even more confusing.
Other cases
removing break
makes the error a little better, but it still would be nice to say "this is a limitation of lifetimes" instead of "your code is broken"
fn foo(app: &mut Command) {
let alias_definition = vec!["submodule", "status"];
let mut cmd = app;
for arg in alias_definition {
if let Some(next) = cmd.find_subcommand_mut(arg) {
cmd = next;
}
}
}
error[E0499]: cannot borrow `*cmd` as mutable more than once at a time
--> src/lib.rs:12:29
|
12 | if let Some(next) = cmd.find_subcommand_mut(arg) {
| ^^^
| |
| `*cmd` was mutably borrowed here in the previous iteration of the loop
| first borrow used here, in later iteration of loop
the recursive case is also pretty confusing, although it looks more like a "traditional" polonius error so maybe it's easier to recognize
use clap::Command; // 4.5.4
fn find_nested_cmd<'a, 'b, 'c>(cmd: &'a mut Command, query: &'b [&'c str]) -> (&'a mut Command, &'b [&'c str]) {
// let next = query.first().and_then(|arg| cmd.find_subcommand_mut(arg));
if let Some(arg) = query.first() {
if let Some(next) = cmd.find_subcommand_mut(arg) {
return find_nested_cmd(next, &query[1..]);
}
}
(cmd, query)
}
fn foo(app: &mut Command) {
let alias_definition = vec!["submodule", "status"];
let (subcmd, args) = find_nested_cmd(app, &alias_definition);
dbg!(subcmd.get_bin_name());
}
error[E0499]: cannot borrow `*cmd` as mutable more than once at a time
--> src/lib.rs:10:6
|
3 | fn find_nested_cmd<'a, 'b, 'c>(cmd: &'a mut Command, query: &'b [&'c str]) -> (&'a mut Command, &'b [&'c str]) {
| -- lifetime `'a` defined here
...
6 | if let Some(next) = cmd.find_subcommand_mut(arg) {
| --- first mutable borrow occurs here
7 | return find_nested_cmd(next, &query[1..]);
| ---------------------------------- returning this value requires that `*cmd` is borrowed for `'a`
...
10 | (cmd, query)
| ^^^ second mutable borrow occurs here
another workaround is to add an extra unwrap() so it doesn't look to the compiler like the lifetimes are related:
for arg in alias_definition {
if let Some(next) = cmd.find_subcommand_mut(arg) {
cmd = cmd.find_subcommand_mut(arg).unwrap();
}
}
Rust Version
rustc 1.80.0-nightly (8c127df75 2024-05-16)
binary: rustc
commit-hash: 8c127df75fde3d5ad8ef9af664962a7676288b52
commit-date: 2024-05-16
host: x86_64-unknown-linux-gnu
release: 1.80.0-nightly
LLVM version: 18.1.4
Anything else?
@rustbot label fixed-by-polonius
Metadata
Metadata
Assignees
Labels
Area: The borrow checkerArea: Messages for errors, warnings, and lintsDiagnostics: Confusing error or lint that should be reworked.Diagnostics: An error or lint that doesn't give enough information about the problem at hand.Relevant to the compiler team, which will review and decide on the PR/issue.Compiling with `-Zpolonius` fixes this issue.