A lot of people are talking about why they think shadowing is fine, but in order to attempt to answer the question:
People don't like shadowing because, in the enterprise context, you often deal with code that is 1) not written by you and 2) a little messier (often longer) than you'd personally like. These two things combine to make shadowing a source of confusion and mental overhead.
Let's take the example you used from another comment:
for rr in sol.sln.rr.iter() {
for rr in rr.iter() {
if !rr.is_finite() {
println!("ERROR: Solution diverged at timestep: {}:{}", iter, m);
break 'outer;
}
}
}
That's all well and good --- it's fairly easy to see that the rr in the if statement is not the same rr from the initial for loop. However, if this function evolves from what it is now to something like:
for rr in sol.sln.rr.iter() {
rr.doStuff();
some_other_function();
maybe_this_one_takes_rr();
// ... 10 more lines
for rr in rr.iter() {
some_other_calls_unrelated_to_rr();
// ... 10 more lines, maybe a loop or conditional
if !rr.is_finite() { // Now --- what is rr and where did it come from? What's its type?
println!("ERROR: Solution diverged at timestep: {}:{}", iter, m);
break 'outer;
}
}
}
Now, by the time you get to the if statement inside the nested loop, in order to properly understand which rr you're talking about, you have to carefully read (and keep in your working memory) the entire function context from the start; you can't just skim from the top for the declaration of the variable. Heck, if you do skim from the top, you might end up thinking rr is a totally different type than it actually is! This is particularly awful for immutable values, since you might see an immutable declaration at the start of a function and assume you know to what data that variable name refers, but then it changes over the course of the function (bc now it's shadowed).
You might counter --- "but this is because of bad coding practice; if you kept your functions small and only carefully used shadowing, you wouldn't have this issue!" You're right. But, the same is true about memory errors in unsafe languages, or type errors in languages like Python or Javascript.
Even though careful programmer discipline can prevent many problems, in the long-term, multiple-maintainer context, relying on careful programmer discipline just isn't sound. Instead, people like language features to forcibly reduce mental overhead and the required program context you have to keep "in [human] memory."
deffind_one(id)
# 中略
relation = if model.composite_primary_key?
where(primary_key.zip(id).to_h)
else
where(primary_key => id)
end
record = relation.take
raise_record_not_found_exception!(id, 0, 1) unless record
record
end
複合キーではない場合と仮定して、ものすごくざっくりと解説してみますが、つまり、whereメソッドで条件に合うものを検索して、そのなかから一つだけを take する。これがfind_oneがやっていることです。
when Stringif rest.empty?
parts = [Arel.sql(opts)]
elsif rest.first.is_a?(Hash) && /:\w+/.match?(opts)
parts = [build_named_bound_sql_literal(opts, rest.first)]
elsif opts.include?("?")
parts = [build_bound_sql_literal(opts, rest)]
else
parts = [Arel.sql(model.sanitize_sql([opts, *rest]))]
end