Ruby truyền tham số thế nào? Pass-by-value của object reference

Ruby thường được mô tả là “pass-by-reference”, nhưng mô tả chính xác hơn là pass-by-value, trong đó value được truyền là reference tới object.

Nghe hơi vòng, nên xem code.

Mutate object

1
2
3
4
5
6
7
def add_item(items)
items << "cat"
end

names = []
add_item(names)
names # => ["cat"]

Parameter items và variable names ban đầu giữ reference tới cùng array. Mutate qua một reference thì cả hai đều quan sát object đã đổi.

Rebind parameter

1
2
3
4
5
6
7
def replace(items)
items = ["dog"]
end

names = []
replace(names)
names # => []

Assignment chỉ rebind local parameter items sang object khác. Variable names bên ngoài không bị rebind. Nếu Ruby thật sự pass variable by reference, ví dụ này đã thay đổi names.

Immutable-looking values

Integer, symbol và nhiều object thường được dùng theo cách immutable:

1
2
3
4
5
6
7
def increment(number)
number += 1
end

value = 1
increment(value)
value # => 1

+= tạo/rebind value mới; nó không mutate integer cũ.

String thì mutable:

1
2
3
4
5
6
7
def shout(text)
text.upcase!
end

message = "hello"
shout(message)
message # => "HELLO"

Mental model

  1. Variable giữ reference tới object.
  2. Method nhận một bản sao của reference đó.
  3. Mutating object được quan sát qua mọi reference.
  4. Rebinding parameter không rebind variable của caller.

Vì vậy câu hỏi khi review method không phải chỉ là “truyền value hay reference?”, mà là method có mutate object được truyền vào không? Dấu ! có thể gợi ý, nhưng Ruby không bắt buộc; API contract mới là thứ phải đọc.