...">

Why do global Ruby strings like $ & ignore mutations without errors?

I am learning Ruby (2.0) and it just surprised me:

s = "1234" s =~ /\d+/ $& ==> "1234" # as expected, $& contains the matched string $&.slice!(-2..-1) # should mutate string $& ==> "1234" # what? s.slice(-2..-1) s ==> "12" # as expected 

The slice! method is supposed to be slice! should mutate the string. Other mutator methods behave the same. My questions are: why does this not cause an error, what do I expect when a function cannot do what it says it will do? Is it somewhere documented? Is there any justification?

Update

So, I see that $& does not act as a global variable. Each reference to it gives a new object, as if it really is a function with no argument:

 irb> $foo = "1234" => "1234" irb> $foo.object_id => 70205012205980 irb> $foo.object_id => 70205012205980 # the same irb> $&.object_id => 70205003531300 irb> $&.object_id => 70205011619040 # different object 

So ... my question becomes: is it just β€œmagic” from the interpreter or $& actually a function with no argument, how could I define in Ruby with def ... end ? And how can I tell the difference? In Python, I could reference the foo function simply by using its name:

 >>> foo <function foo at 0x10d3117d0> 

Is there any way to do this in Ruby? Then I could see what $ & "really" is (if it's not magic).

+6
source share
3 answers

The Ruby C API includes connected and virtual variables. From README.EXT :

You can define bound variables. Access functions (getter and setter) are called when accessing the bound variables.

 void rb_define_hooked_variable(const char *name, VALUE *var, VALUE (*getter)(), void (*setter)()) 

If you need to setter or getter, just put 0 for the hook you do not need. If both hooks are 0, rb_define_hooked_variable () works the same as rb_define_variable ().

The prototypes of the receiver and setter functions are as follows:

 VALUE (*getter)(ID id, VALUE *var); void (*setter)(VALUE val, ID id, VALUE *var); 

You can also define a global Ruby variable without the corresponding C variable. The value of the variable will be set / obtained only with the help of hooks.

  void rb_define_virtual_variable(const char *name, VALUE (*getter)(), void (*setter)()) 

The prototypes of the receiver and setter functions are as follows:

 VALUE (*getter)(ID id); void (*setter)(VALUE val, ID id); 

$& is an example of a virtual variable, it is defined in re.c , the corresponding receiver is last_match_getter and there is no associated installer.

So, $& is, in a sense, a no-arg function, only it is implemented in C. You cannot (as far as I know) define your own virtual globals like this in pure Ruby (you can if you create your own extension C)

+5
source

Since the purpose of $& is to tell you what was matched, it would be prudent that matching should be the only operation that can update it.

+2
source

This is an interesting question. Jeconner's answer and many comments on this question simply say that this is not useful to do, but it does not answer the question. Regardless of whether this should be done in practical code, the OP question still remains.

The answer to the question is that it looks like a function for read-only strings. When you apply a destructive operation to a read-only string, it returns another string object. And I agree with the OP that this is strange. This should most likely cause an error.

If someone does not come up with a convincing explanation, I suggest you request it on the Ruby core and / or request a function to raise the error.

+1
source

All Articles