of course, all lints can have false positives (and false negatives), and should be treated as guide lines, other than rules. that's why they are lints, not compile diagnoses.
that being said, I think the situations where shadow_unrelated
would reject good code is rare, and should be treated as exceptions, case by case. and the example in your playground link is too trivial to prove the point. arguably, I would not say it's idiomatic at all, let alone the already mentioned unused
rustc lint.
the rust idiom of reusing the same variable name frees programmers from having to think of different names for semantically similar or related variables.
here's an example to demonstrate what I mean:
/// user can specify a config file using `--config PATH` command line option
fn load_config(config: Option<&Path>) -> Config {
let config = config.unwrap_or(DEFAULT_CONFIG_PATH);
let Ok(config) = std::fs::read(config) else {
return Config::default();
};
let config = json::from_slice::<json::Value>(&config).unwrap();
match config.get("version").as_i64() {
Some(1) => {
let config: ConfigV1 = json::from_value(config).unwrap();
config.into()
}
Some(2) => {
let config: ConfigV2 = json::from_value(config).unwrap();
config.into()
}
_ => {
warn!("unsupported config file version");
Config::default()
}
}
}
in languages where you cannot use the same name for different variables in scope, you are forced to give different names for the same piece of information (pseudo code):
/// `config_file_path` is specified at command line and is optional
function load_config(config_file_path: Path) -> Config {
var config_file = if config_file_path != null {
open_file(config_file_path)
} else {
open_file(DEFAULT_CONFIG_FILE)
}
var config_string = config_file.read_all();
var config_json = parse_json(config_string);
if config_json["version"] == 1 {
var config_v1 = config_v1_from_json(config_json);
return migrate_config_from_v1(config_v1);
} else if config_json["version"] == 2 {
return config_v2_from_json(config_json);
} else {
return default_config();
}
}
naming is hard, people tend to avoid naming things, so they might omit the intermediate variables all along and chain all the functions and methods together, which can be equally bad to read (unless the API is purposely designed to be used like this); careful code formatting can help a bit, but not solve the problem:
config_from_json(
parse_json(
open_file(
config_file_path || DEFAULT_CONFIG_FILE
)
.readall()
)
)
granted, this example is simple and contrived, but it is not far from real code (I have seen very simiar code in the wild).
for this example, I'm pretty sure it will NOT trigger the shadow_unrelated
lint. (but note also, not all variables with the same names are technically "shadowing" in rust, because of linear types, but most other languges don't have linear types either).
to summarize:
- the
shadow_unrelated
lint, like all lints, should be treated as a guide or hint, to look for potential bad code, not as a strict rule to follow
- but I think overall it is a very useful one: as its name suggested, it is meant to catch "unrelated" shadows, and in my opinion, false positives should NOT be very common in practice.
- rust allows (and even encourages, when appropriate,) to use the same name for multiple variables, however, this doesn't mean absuing the same variable name multiple times is always considered idiomatic.