Ruby pass по ссылке или по значению?

@user.update_languages(params[:language][:language1], 
                       params[:language][:language2], 
                       params[:language][:language3])
lang_errors = @user.errors
logger.debug "--------------------LANG_ERRORS----------101-------------" 
                + lang_errors.full_messages.inspect

if params[:user]
  @user.state = params[:user][:state]
  success = success & @user.save
end
logger.debug "--------------------LANG_ERRORS-------------102----------" 
                + lang_errors.full_messages.inspect

if lang_errors.full_messages.empty?

12 ответов


в традиционной терминологии, Ruby строго pass-by-value. Но на самом деле ты спрашиваешь не об этом.

Ruby не имеет понятия о чистом, не ссылочном значении, поэтому вы, конечно, не можете передать его методу. Переменные всегда являются ссылками на объекты. Чтобы получить объект, который не изменится из-под вас, вам нужно dup или клонировать объект, который вы передали, таким образом, давая объект, на который никто больше не имеет ссылки. (Даже это не однако пуленепробиваемый-оба стандартных метода клонирования делают мелкую копию, поэтому переменные экземпляра клона все еще указывают на те же объекты, что и оригиналы. Если объекты, на которые ссылается ivars, мутируют, это все равно будет отображаться в копии, поскольку он ссылается на те же объекты.)


другие ответчики все правы, но друг попросил меня объяснить ему это, и что на самом деле сводится к тому, как Ruby обрабатывает переменные, поэтому я подумал, что поделюсь некоторыми простыми картинками / объяснениями, которые я написал для него (извинения за длину и, вероятно, некоторое упрощение):


Q1: что происходит, когда вы назначаете новую переменную str значение 'foo'?

str = 'foo'
str.object_id # => 2000

enter image description here

A: ярлык под названием str is создано, что указывает на объект 'foo', который для состояния этого интерпретатора Ruby находится в местоположении памяти 2000.


Q2: что происходит, когда вы назначаете существующей переменной str к новому объекту с помощью =?

str = 'bar'.tap{|b| puts "bar: #{b.object_id}"} # bar: 2002
str.object_id # => 2002

enter image description here

A: этикетка str теперь указывает на другой объект.


Q3: что происходит, когда вы назначаете новую переменную = to str?

str2 = str
str2.object_id # => 2002

enter image description here

A: новый ярлык под названием str2 создается, что указывает на тот же объект as str.


Q4: что произойдет, если объект, на который ссылается str и str2 изменится?

str2.replace 'baz'
str2 # => 'baz'
str  # => 'baz'
str.object_id # => 2002
str2.object_id # => 2002

enter image description here

A: обе метки по-прежнему указывают на один и тот же объект, но сам этот объект мутировал (его содержимое изменилось, чтобы быть чем-то еще.)


как это относится к изначальному вопросу?

это в основном то же самое, что и в Q3/Q4; метод получает свою собственную частную копию переменной / метки (str2), который передается в него (str). Он не может изменить, какой объект метка str указывает на, но он может изменить содержание объект, на который они оба ссылаются, чтобы содержать else:

str = 'foo'

def mutate(str2)
  puts "str2: #{str2.object_id}"
  str2.replace 'bar'
  str2 = 'baz'
  puts "str2: #{str2.object_id}"
end

str.object_id # => 2004
mutate(str) # str2: 2004, str2: 2006
str # => "bar"
str.object_id # => 2004

является ли Ruby pass по ссылке или по значению?

Ruby-это значение pass-by-value. Всегда. Ни одно исключение. Нет сослагательного наклонения. Никаких "но".

вот простая программа, которая демонстрирует этот факт:

def foo(bar)
  bar = 'reference'
end

baz = 'value'

foo(baz)

puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value

Ruby использует "pass by object reference"

(используя терминологию Python.)

сказать, что Ruby использует " pass by value "или" pass by reference", на самом деле недостаточно описательно, чтобы быть полезным. Я думаю, что большинство людей знают это в наши дни, что терминология ("значение" против "ссылки") происходит от C++.

в C++ "pass by value" означает, что функция получает копию переменной, и любые изменения в копии не изменяют оригинал. Это верно и для объектов. Если вы пройдете переменная объекта по значению, тогда весь объект (включая все его члены) копируется, и любые изменения членов не изменяют эти члены в исходном объекте. (Это другое, если вы передаете указатель по значению, но у Ruby все равно нет указателей, AFAIK.)

class A {
  public:
    int x;
};

void inc(A arg) {
  arg.x++;
  printf("in inc: %d\n", arg.x); // => 6
}

void inc(A* arg) {
  arg->x++;
  printf("in inc: %d\n", arg->x); // => 1
}

int main() {
  A a;
  a.x = 5;
  inc(a);
  printf("in main: %d\n", a.x); // => 5

  A* b = new A;
  b->x = 0;
  inc(b);
  printf("in main: %d\n", b->x); // => 1

  return 0;
}

выход:

in inc: 6
in main: 5
in inc: 1
in main: 1

в C++ "pass by reference" означает, что функция получает доступ к исходной переменной. Он может назначить целое новое литеральное целое число, и исходная переменная будет иметь и это тоже.

void replace(A &arg) {
  A newA;
  newA.x = 10;
  arg = newA;
  printf("in replace: %d\n", arg.x);
}

int main() {
  A a;
  a.x = 5;
  replace(a);
  printf("in main: %d\n", a.x);

  return 0;
}

выход:

in replace: 10
in main: 10

Ruby использует pass by value (в смысле C++), если аргумент не является объектом. Но в Ruby все является объектом, поэтому в Ruby действительно нет значения pass by в смысле C++.

в Ruby используется" pass by object reference " (для использования терминологии Python):

  • внутри функции любой из членов объекта может иметь новые значения, назначенные им, и эти изменения будут сохраняться после функция возвращается.*
  • внутри функции назначение совершенно нового объекта переменной приводит к тому, что переменная перестает ссылаться на старый объект. Но после возвращения функции исходная переменная все равно будет ссылаться на старый объект.

поэтому Ruby не использует "pass by reference" в смысле C++. Если это так, то назначение нового объекта переменной внутри функции приведет к тому, что старый объект будет забыт после функции возвращенный.

class A
  attr_accessor :x
end

def inc(arg)
  arg.x += 1
  puts arg.x
end

def replace(arg)
  arg = A.new
  arg.x = 3
  puts arg.x
end

a = A.new
a.x = 1
puts a.x  # 1

inc a     # 2
puts a.x  # 2

replace a # 3
puts a.x  # 2

puts ''

def inc_var(arg)
  arg += 1
  puts arg
end

b = 1     # Even integers are objects in Ruby
puts b    # 1
inc_var b # 2
puts b    # 1

выход:

1
2
2
3
2

1
2
1

* вот почему в Ruby, если вы хотите изменить объект внутри функции, но забыть эти изменения, когда функция возвращается, то вы должны явно сделать копию объекта, прежде чем вносить временные изменения в копию.


уже есть несколько отличных ответов, но я хочу опубликовать определение пары авторитетов по этому вопросу, но также надеюсь, что кто-то может объяснить, что означали авторитеты Matz (создатель Ruby) и Дэвид Фланаган в своей превосходной книге О'Рейли,Язык Программирования Ruby.

[из 3.8.1: ссылки на объекты]

когда вы передаете объект методу в Ruby, это ссылка на объект, которая передается методу. Он это не сам объект, а не Ссылка на ссылку на объект. Другой способ сказать, что аргументы метода передаются по стоимости, а не по ссылке, но что переданные значения являются ссылками на объекты.

поскольку ссылки на объекты передаются методам, методы могут использовать эти ссылки для изменения базового объекта. Эти изменения затем отображаются при возврате метода.

Это все имеет смысл для меня до последнего абзаца, и особенно последнее предложение. Это в лучшем случае вводит в заблуждение, а в худшем приводит в замешательство. Как, в любом случае, изменения в этой переданной по значению ссылке могут изменить базовый объект?


Ruby является pass-by-value в строгом смысле, но значения являются ссылками.

Это можно назвать "pass-reference-by-value". Эта статья имеет лучшее объяснение, которое я прочитал: http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/

Pass-reference-by-value можно кратко объяснить следующим образом:

функция получает ссылку на (и открыть) один и тот же объект в памяти в качестве звонка. Однако он не получает поле, в котором вызывающий объект хранит этот объект; как и в pass-value-by-value, функция предоставляет свой собственный ящик и создает для себя новую переменную.

результирующее поведение на самом деле представляет собой комбинацию классических определений pass-by-reference и pass-by-value.


является ли Ruby pass по ссылке или по значению?

Ruby является pass-by-reference. Всегда. Ни одно исключение. Нет сослагательного наклонения. Никаких "но".

вот простая программа, которая демонстрирует этот факт:

def foo(bar)
  bar.object_id
end

baz = 'value'

puts "#{baz.object_id} Ruby is pass-by-reference #{foo(baz)} because object_id's (memory addresses) are always the same ;)"

=> 2279146940 Ruby является pass-by-reference 2279146940, потому что object_id (адреса памяти) всегда одинаковы;)

def bar(babar)
  babar.replace("reference")
end

bar(baz)

puts "some people don't realize it's reference because local assignment can take precedence, but it's clearly pass-by-#{baz}"

=> некоторые люди не понимают, что это ссылка, потому что местное назначение может возьмите приоритет, но это явно pass-by-reference


параметры являются копией исходной ссылки. Таким образом, вы можете изменить значения, но не можете изменить исходную ссылку.


Ruby-интерпретируемый язык. Переменные ссылками на данные, но не сами данные. Это облегчает использование одной и той же переменной для данных разных типов.

назначение lhs = rhs затем копирует ссылку на rhs, а не данные. Это отличается в других языках, таких как C, где назначение делает копию данных в lhs из rhs.

поэтому для вызова функции переданная переменная, скажем x, действительно копируется в локальную переменную в функции, но x является ссылка. Затем будет две копии ссылки, обе ссылающиеся на одни и те же данные. Один будет в вызывающем, один в функции.

назначение в функции затем скопирует новую ссылку на версию функции x. После этого версия вызывающего X остается неизменной. Это еще ссылка на исходные данные.

напротив, используя .метод replace на x заставит ruby сделать копию данных. Если replace используется перед любыми новыми назначениями тогда действительно вызывающий абонент также увидит изменение данных в своей версии.

аналогично, пока исходная ссылка находится в такте для переданной переменной, переменные экземпляра будут такими же, что и вызывающий объект. В рамках объекта переменные экземпляра всегда имеют самые последние ссылочные значения, независимо от того, предоставлены ли они вызывающим объектом или установлены в функции, в которую был передан класс.

"вызов по значению" или "вызов по ссылке" путаница здесь из-за путаницы над " = "на скомпилированных языках" = " является копией данных. Здесь на этом интерпретируемом языке ' = ' является ссылочной копией. В Примере у вас есть ссылка, переданная вслед за ссылочной копией, хотя"=", которая колотит оригинал, переданный в ссылке, а затем люди говорят об этом, как будто " = " была копией данных.

чтобы соответствовать определениям, мы должны придерживаться '.заменить " как копию данных. С точки зрения '.заменить' мы видим, что это действительно проходит по ссылке. Кроме того, если мы пройдем через отладчик, мы увидим, что ссылки передаются, поскольку переменные являются ссылками.

однако, если мы должны сохранить ' = ' в качестве системы отсчета, то действительно мы получаем, чтобы увидеть переданные данные до назначения, а затем мы больше не видим его после назначения, в то время как данные вызывающего абонента остаются неизменными. На поведенческом уровне это передается по значению, пока мы не считаем, что переданное значение composite-поскольку мы не сможем сохранить часть его при изменении другой части в одном назначении (поскольку это назначение изменяет ссылку, а оригинал выходит за рамки). Также будет бородавка, в этом случае переменные в объектах будут ссылками, как и все переменные. Поэтому мы будем вынуждены говорить о передаче "ссылок по значению" и использовать связанные с этим выражения.


попробуйте это:--

1.object_id
#=> 3

2.object_id
#=> 5

a = 1
#=> 1
a.object_id
#=> 3

b = 2
#=> 2
b.object_id
#=> 5

идентификатор a содержит object_id 3 для объекта значения 1, а идентификатор b содержит object_id 5 для объекта значения 2.

вот этого:--

a.object_id = 5
#=> error

a = b
#value(object_id) at b copies itself as value(object_id) at a. value object 2 has object_id 5
#=> 2

a.object_id 
#=> 5

теперь a и b содержат один и тот же object_id 5, который ссылается на объект значения 2. Таким образом, переменная Ruby содержит object_ids для ссылки на объекты value.

выполнение следующего также дает ошибку:--

c
#=> error

но делать это не даст ошибка:--

5.object_id
#=> 11

c = 5
#=> value object 5 provides return type for variable c and saves 5.object_id i.e. 11 at c
#=> 5
c.object_id
#=> 11 

a = c.object_id
#=> object_id of c as a value object changes value at a
#=> 11
11.object_id
#=> 23
a.object_id == 11.object_id
#=> true

a
#=> Value at a
#=> 11

здесь идентификатор a возвращает объект значения 11, чей идентификатор объекта 23, т. е. object_id 23 находится в идентификаторе a, теперь мы видим пример с помощью метода.

def foo(arg)
  p arg
  p arg.object_id
end
#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23

arg в foo присваивается возвращаемое значение x. Он ясно показывает, что аргумент передается значением 11, а значение 11, являющееся объектом, имеет уникальный идентификатор объекта 23.

теперь вижу это:--

def foo(arg)
  p arg
  p arg.object_id
  arg = 12
  p arg
  p arg.object_id
end

#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23
#=> 12
#=> 25
x
#=> 11
x.object_id
#=> 23

здесь идентификатор arg сначала содержит object_id 23 для ссылки на 11 и после внутреннего назначения со значением object 12 он содержит object_id 25. Но он не изменяет значение, на которое ссылается идентификатор x, используемый в методе вызова.

следовательно, Ruby передается по значению, а переменные Ruby не содержат значений, но содержат ссылку на объект value.


следует отметить, что вам даже не нужно использовать метод "replace" для изменения исходного значения. Если вы назначаете одно из хэш-значений для хэша, вы изменяете исходное значение.

def my_foo(a_hash)
  a_hash["test"]="reference"
end;

hash = {"test"=>"value"}
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"

Two references refer to same object as long as there is no reassignment. 

любые обновления в том же объекте не будут ссылаться на новую память, поскольку она все еще находится в той же памяти. Вот несколько примеров :

    a = "first string"
    b = a



    b.upcase! 
    => FIRST STRING
    a
    => FIRST STRING

    b = "second string"


a
    => FIRST STRING
    hash = {first_sub_hash: {first_key: "first_value"}}
first_sub_hash = hash[:first_sub_hash]
first_sub_hash[:second_key] = "second_value"

    hash
    => {first_sub_hash: {first_key: "first_value", second_key: "second_value"}}

    def change(first_sub_hash)
    first_sub_hash[:third_key] = "third_value"
    end

    change(first_sub_hash)

    hash
    =>  {first_sub_hash: {first_key: "first_value", second_key: "second_value", third_key: "third_value"}}