Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
382 views
in Technique[技术] by (71.8m points)

ruby - Where and how is the _ (underscore) variable specified?

Most are aware of _’s special meaning in IRB as a holder for last return value, but that is not what I'm asking about here.

Instead, I’m asking about _ when used as a variable name in plain-old-Ruby-code. Here it appears to have special behavior, akin to a “don't care variable” (à la Prolog). Here are some useful examples illustrating its unique behavior:

lambda { |x, x| 42 }            # SyntaxError: duplicated argument name
lambda { |_, _| 42 }.call(4, 2) # => 42
lambda { |_, _| 42 }.call(_, _) # NameError: undefined local variable or method `_'
lambda { |_| _ + 1 }.call(42)   # => 43
lambda { |_, _| _ }.call(4, 2)  # 1.8.7: => 2
                                # 1.9.3: => 4
_ = 42
_ * 100         # => 4200
_, _ = 4, 2; _  # => 2

These were all run in Ruby directly (with putss added in)—not IRB—to avoid conflicting with its additional functionality.

This is all a result of my own experimentation though, as I cannot find any documentation on this behavior anywhere (admittedly it's not the easiest thing to search for). Ultimately, I'm curious how all of this works internally so I can better understand exactly what is special about _. So I’m asking for references to documentation, and, preferably, the Ruby source code (and perhaps RubySpec) that reveal how _ behaves in Ruby.

Note: most of this arose out of this discussion with @Niklas B.

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There is some special handling in the source to suppress the "duplicate argument name" error. The error message only appears in shadowing_lvar_gen inside parse.y, the 1.9.3 version looks like this:

static ID
shadowing_lvar_gen(struct parser_params *parser, ID name)
{
    if (idUScore == name) return name;
    /* ... */

and idUScore is defined in id.c like this:

REGISTER_SYMID(idUScore, "_");

You'll see similar special handling in warn_unused_var:

static void
warn_unused_var(struct parser_params *parser, struct local_vars *local)
{
    /* ... */
    for (i = 0; i < cnt; ++i) {
        if (!v[i] || (u[i] & LVAR_USED)) continue;
        if (idUScore == v[i]) continue;
        rb_compile_warn(ruby_sourcefile, (int)u[i], "assigned but unused variable - %s", rb_id2name(v[i]));
    }
}

You'll notice that the warning is suppressed on the second line of the for loop.

The only special handling of _ that I could find in the 1.9.3 source is above: the duplicate name error is suppressed and the unused variable warning is suppressed. Other than those two things, _ is just a plain old variable like any other. I don't know of any documentation about the (minor) specialness of _.

In Ruby 2.0, the idUScore == v[i] test in warn_unused_var is replaced with a call to is_private_local_id:

if (is_private_local_id(v[i])) continue;
rb_warn4S(ruby_sourcefile, (int)u[i], "assigned but unused variable - %s", rb_id2name(v[i]));

and is_private_local_id suppresses warnings for variables that begin with _:

if (name == idUScore) return 1;
/* ... */
return RSTRING_PTR(s)[0] == '_';

rather than just _ itself. So 2.0 loosens things up a bit.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...