Roman Pushkin
Roman Pushkin

Reputation: 6079

Exclamation mark and assignment inside of a function

Can someone explain why this:

def do_something str
  str = "bar"
end
​
str_main = "foo"
do_something str_main
​
puts str_main

displays foo?

And this:

def do_something str
  str.capitalize!
end
​
str_main = "foo"
do_something str_main
​
puts str_main

displays Foo?

Upvotes: 0

Views: 97

Answers (4)

user3373470
user3373470

Reputation:

All ruby methods return the last thing evaluated. However, object assignment stays within the scope of the current code block. Assigning str_main to a new value within a method will not affect str_main, unless it was an instance variable (@str_main). Doing such allows you to reassign an object across scopes, or depths, of your program. This is why your first method outputs 'foo' instead of 'bar'.

Now, the second example. #capitalize is a method called on a string object. It returns a new String instance, where its value is original object capitalized.

string = 'foobar'
string.capitalize  # => 'Foobar'
puts string  # => 'foobar'

Notice how string is only modified temporarily, and when called again it is back to normal.

Many methods in ruby have counterparts ending in !. This convention is the same as: object = object.some_method. Instead of creating a new instance of an object, these methods edit the original object's value. In the case of #capitalize!, the string is capitalized and modified for future calls.

string = 'foo'
string.capitalize!  # => 'Foo'
puts string  # => 'Foo'

Back to your second example. Using the #capitalize! method within the scope of do_something allows you to modify the str_main object. In a similar way to making str_main an instance variable.

Upvotes: 1

ndnenkov
ndnenkov

Reputation: 36100

Because of the way Ruby passes arguments.

When the method is being called, you have two references, str_main and str, to the same object "foo".

In the first example, when you use str = "bar", you are just changing what the str reference points to. So now you have str_main -> "foo" and str -> "bar". Therefore, the original object is not changed.

In the second example, you didn't change the str reference and changed the string in place with a mutator method, thus changing the same object that str_main points to.

Upvotes: 3

Wand Maker
Wand Maker

Reputation: 18762

In Ruby, references are passed by value. So, a reference to str_main is passed to method do_something, a copy of reference is present in variable str.

This, however, does not mean that value that is referred to by both variables also has been copied around - there is still a single copy of referred to value, which is the string defined in Main.

Hence, when you assign a new value to str, this does not alter the value of str_main. However, when you modify the value that is referred by str, its changes are visibble outside.

Upvotes: 1

Richard Hamilton
Richard Hamilton

Reputation: 26444

The exclamation mark or bang operator modifies the original value. It is a destructive method. For example, let's say you had a string

string = "hi";

If you call the upcase method, you will get the following

string.upcase
=> "HI"

However, if you call string again, you will get the initial value.

string
=> "hi"

Now, let's say you use the destructive method upcase!

string.upcase!
=> "HI"

Now, if you call string again, you will see that the value was mutated.

string
=> "HI"

Upvotes: 1

Related Questions