Ruby: lambda, proc и yield — три грани одного ужаса

В Ruby работа с блоками кода — фундаментальный навык для любого разработчика. Понимание разницы между lambda, proc и yield критически важно для написания чистого, эффективного и поддерживаемого кода. В этой статье мы детально разберём ключевые отличия, особенности поведения и практические сценарии применения каждого подхода.


🧠 Три способа вызвать блок кода

def with_yield
  yield
end

def with_proc(p)
  p.call
end

with_yield { puts "через yield" }         # => через yield
with_proc(Proc.new { puts "через proc" }) # => через proc

🤹‍♂️ Различия между lambda и Proc.new

1. Поведение return

def l
  f = lambda { return "lambda" }
  f.call
  "после lambda"
end

def p
  f = Proc.new { return "proc" }
  f.call
  "после proc"
end

puts l # => "после lambda"
puts p # => LocalJumpError (или nil, если rescue)

lambda: return выходит из lambda proc: return выходит из окружающего метода!


2. Проверка аргументов

l = ->(x, y) { "#{x}, #{y}" }
p = Proc.new { |x, y| "#{x}, #{y}" }

puts l.call(1)         # ArgumentError!
puts p.call(1)         # => "1, "

lambda — строгая: проверяет кол-во аргументов. proc — снисходительная: недостающие аргументы = nil.


🚀 Синтаксис

lambda { }             # обычная форма
->(x, y) { x + y }      # сокращённая
Proc.new { }           # старый стиль
proc { }               # alias

Современные проекты предпочитают ->.


🧷 А yield?

  • Вызывает переданный блок, без явного параметра.
  • Работает только если был передан блок.
def do_something
  yield if block_given?
end

do_something { puts "Окей!" }

💥 Попробуй вызвать yield, если блока нет — будет LocalJumpError.


📌 Практика VS собеседование

Характеристика lambda Proc.new yield
return внутри не влияет на метод выходит из метода выходит из метода
Аргументы строго мягко зависит от сигнатуры
Доступен как объект? да да нет
Можно передать в переменную да да нет

💡 Совет: всегда называй блоки, когда нужен контроль

def wrap(&block)
  block.call if block
end

Доступен как объект?

🔹 lambda и Proc.new:

Они создают объекты, которые можно присвоить в переменную:

f = lambda { puts "Hi" }
f.call
p = Proc.new { puts "Hello" }
p.call

→ Это именованные объекты (f, p) — переменные, которые хранят блок и дают к нему доступ.


🔸 yield:

yield — это безымянный вызов блока, переданного в метод. Блок не получает имени по умолчанию:

def greet
  yield if block_given?
end

greet { puts "Yo" }

→ У нас нет переменной, ссылающейся на этот блок. Если хочешь дать имя — надо явно использовать &block:

def greet(&block)
  block.call
end

💡 Резюме:

  • lambda и proc: объекты, которые ты можешь назвать и передавать
  • yield: “встроенный” вызов анонимного блока, без доступа к объекту этого блока

🔚 Вывод: lambda и proc — братья с разным воспитанием. А yield — это просто безымянный гость. Разница кажется мелкой, пока return не устроит саботаж твоему методу.

🗓 Дата публикации: 05.03.2023, но это не точно...

lambda proc yield Ruby блоки кода разница