-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
clippy::option_if_let_else suggests hard code #8829
Comments
You can't see the mapping argument in the suggestion because it's cut off. I think it wants you to do this: fn run(job: &str) -> Output {
job.strip_prefix("ok:").map_or_else(||
if let Some(stripped) = job.strip_prefix("ko:") {
Output::JobError(stripped.into(), "".into())
} else if let Some(stripped) = job.strip_prefix("crash:") {
Output::ProcessError(stripped.into())
} else {
unreachable!(
"Job should begin with ok:, ko, or crash: (actual: '{}')",
job
)
},
|stripped| Output::Success(stripped.into(), "".into()))
} Once that's done it will start warning about the left over |
So this lint suggests fn run(&self, job: &str) -> Output {
job.strip_prefix("ok:").map_or_else(
|| {
job.strip_prefix("ko:").map_or_else(
|| {
job.strip_prefix("crash:").map_or_else(
|| {
unreachable!(
"Job should begin with ok:, ko, or crash: (actual: '{}')",
job
)
},
|stripped| Output::ProcessError(stripped.to_string()),
)
},
|stripped| Output::JobError(stripped.to_string(), String::default()),
)
},
|stripped| Output::Success(stripped.to_string(), String::default()),
)
} over fn run(&self, job: &str) -> Output {
if let Some(stripped) = job.strip_prefix("ok:") {
Output::Success(stripped.to_string(), String::new())
} else if let Some(stripped) = job.strip_prefix("ko:") {
Output::JobError(stripped.to_string(), String::new())
} else if let Some(stripped) = job.strip_prefix("crash:") {
Output::ProcessError(stripped.to_string())
} else {
panic!(
"Job should begin with ok:, ko, or crash: (actual: '{}')",
job
)
}
} And I must say I don't find the first gist nice or readable, because :
First, is there a more idiomatic way of doing what I did ? (this gist is a test impl). Second, is there a way to improve clippy ? How could I help ? |
The problem is not about a false positive, so I renamed and retag, but I don't know how to tag this |
Here's a similar example, also involving /* Clippy's suggested version, 13 lines, hard to follow, more nested */
line.strip_prefix("fold along ").map_or_else(
|| {
if !line.is_empty() {
let (a, b) = line
.split_once(',')
.expect("comma-separated pair of integers");
grid.insert((a.parse::<u16>().unwrap(), b.parse::<u16>().unwrap()));
}
},
|fold| {
folds.push(fold.split_once('=').unwrap());
},
); /* Original code, 8 lines, easier to follow, less indentation */
if let Some(fold) = line.strip_prefix("fold along ") {
folds.push(fold.split_once('=').unwrap());
} else if !line.is_empty() {
let (a, b) = line
.split_once(',')
.expect("comma-separated pair of integers");
grid.insert((a.parse::<u16>().unwrap(), b.parse::<u16>().unwrap()));
} |
I also agree fn handle_resolve(limits: &limits::Limits, limiter_id: String, user_id: String) -> Response {
if let Some(limiter) = limits.get_limiter(&limiter_id) {
limiter.resolve(user_id);
Response::ResolveDone
} else {
Response::ErrorLimiterNotFound
}
} fn handle_resolve(limits: &limits::Limits, limiter_id: String, user_id: String) -> Response {
limits.get_limiter(&limiter_id).map_or(Response::ErrorLimiterNotFound, |limiter| {
limiter.resolve(user_id);
Response::ResolveDone
})
} The first one has less cognitive load. However, the lint's example is good: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else |
Imho, it would be better to never suggest The let _ = if let Some(foo) = optional {
foo
} else {
let y = do_complicated_function();
y*y
};
// suggested in the docs:
let _ = optional.map_or_else(||{
let y = do_complicated_function();
y*y
}, |foo| foo);
// suggested as rustfmt actually formats it - one line more than the `if let ... else`
let _ = optional.map_or_else(
|| {
let y = do_complicated_function();
y * y
},
|foo| foo,
); The original // maybe better...?
let _ = match optional {
Some(foo) => foo,
None => {
let y = do_complicated_function();
y*y
}
} |
In general, I would hesitate to use map_or_else when either branch is side-effectful (assertions, I/O, etc), because I that feel that obscures the control flow too much and control flow matters a lot in the presence of such operations. |
// suggested in the docs:
let _ = optional.map_or_else(||{
let y = do_complicated_function();
y*y
}, |foo| foo); should just be let _ = optional.unwrap_or_else(|| {
let y = do_complicated_function();
y * y
}); (as formatted by rustfmt). So that's not a great example to be in the docs. Especially since there isn't a follow up lint that suggests this simplification. |
Not sure if this is relevant here or if I should raise a new issue, but I think when the Before:
Current recommendation:
Alternative:
|
Or maybe there should be a new stylistic/pedantic lint for |
Such a lint is implemented in #11796 but not merged yet. |
Summary
I guess clippy::option_if_let_else should not be suggested here, because the branch yields an Output variant, and not a string slice as suggested.
Lint Name
clippy::option_if_let_else
Reproducer
I tried this code in rust playground:
I saw this happen:
I expected to see this happen:
???
Version
Additional Labels
No response
The text was updated successfully, but these errors were encountered: