2

Is this a ruby bug?

target_url_to_edit = target_url

if target_url_to_edit.include?("http://")
  target_url_to_edit["http://"] = ""
end

logger.debug "target url is now #{target_url}"

This returns target_url without http://

4 Answers 4

10

You need to duplicate the in-memory object because variable names are just references to in-memory objects:

target_url_to_edit = target_url.dup

Now target_url_to_edit gets assigned a new copy of the original object.

For your case this code probably does the same in just one line (no dup, no if):

target_url_to_edit = target_url.sub(%r{^http://}, "")
Sign up to request clarification or add additional context in comments.

Comments

5

No, this is not a bug in Ruby, this is just how shared mutable state works, not just in Ruby but in any programming language.

Think about it this way: my mom calls me "son", my friends call me "Jörg". If I cut my hair, then it doesn't matter which name you use to refer to me: I am the same person, regardless of whether you call me "son" or "Jörg" or "Mr. Mittag" or "hey, douchebag", therefore my hair will always be short. It doesn't magically grow back if you call me by a different name.

The same thing happens in your code: you refer to the string by two different names, but it doesn't matter which name you use; if the string changes, then it changes.

The solution is, of course, to not share mutable state and to not mutate shared state, like in @hurikhan77's answer.

1 Comment

For "but in any programming language" you need to know that Ruby has no primitive types which would be simply copied during assignment. Everything is an object, which is like just assigning a pointer to an object in most other languages.
0

That is not a bug. It is the intended behavior because target_url_to_edit points to the same object in memory as target_url since Ruby uses references for object assignment. If you know C, it is similar to pointers.

Comments

-1

Here is how to change its behaviour to force passing by value (note the star sign):

target_url_to_edit = *target_url.to_s

if target_url_to_edit.include?("http://")
  target_url_to_edit["http://"] = ""
end

logger.debug "target url is now #{target_url}"

And just like many things in ruby, hard to find where it's documented...

3 Comments

I don't think the star is what you want. It will convert a normal object into an array. Just target_url.to_s should be sufficient to get a new string. And it's hard to find where what is documented? The fact that Ruby variables are references?
Can you explain what this has to do with pass-by-value? First off, Ruby is always pass-by-value. There is no need to "force passing by value" and in fact there is no way to force passing by value because pass-by-value is the only evaluation strategy supported by Ruby anyway. And secondly, this has nothing to do with the evaluation strategy. It's a simple consequence of shared mutable state. We have been taught for 60 years that shared mutable state is bad, we have ignored that advice and used shared mutable state anyway for 60 years, and thus we have to live with consequences like this.
Ruby is pass-by-value always, there's nothing else supported. But to the unexperienced programmer it looks like pass-by-reference. In fact, by using "." you are dereferencing the object pointer. Using the [] operator just is a method call identical to var.[]("http://")...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.