Spec-Zone.ru › Ruby 3.4

Методы

Методы реализуют функциональность вашей программы. Вот простое определение метода:

def one_plus_one
  1 + 1
end

Определение метода состоит из ключевого слова def, имени метода, тела метода, значения return и ключевого слова end. При вызове метод выполнит тело метода. Этот метод возвращает 2.

Начиная с Ruby 3.0, для методов, состоящих ровно из одного выражения, также доступен сокращённый синтаксис:

def one_plus_one = 1 + 1

В этом разделе рассматривается только определение методов. См. также документацию по синтаксису вызова методов.

Имена методов

Имена методов могут быть операторами или должны начинаться с буквы либо символа с установленным восьмым битом. Имя может содержать буквы, цифры, _ (подчёркивание или нижнее подчёркивание) либо символ с установленным восьмым битом. Принято разделять слова в составных именах методов символом подчёркивания:

def method_name
  puts "use underscores to separate words"
end

Программы на Ruby должны быть написаны в кодировке, совместимой с US-ASCII, например UTF-8, ISO-8859-1 и т. д. В таких кодировках установленный восьмой бит указывает на расширенный символ. Ruby позволяет использовать такие символы в именах методов и других идентификаторах. В программах на Ruby нельзя использовать некоторые символы, например ASCII NUL (\x00).

Ниже приведены примеры допустимых методов Ruby:

def hello
  "hello"
end

def こんにちは
  puts "means hello in Japanese"
end

Как правило, имена методов совместимы с US-ASCII, поскольку соответствующие клавиши есть на всех клавиатурах.

Имена методов могут заканчиваться на ! (восклицательный знак), ? (вопросительный знак) или = (знак равенства).

Методы с восклицательным знаком (! в конце имени) вызываются и выполняются так же, как любые другие методы. Однако по соглашению метод с восклицательным знаком считается опасным. В стандартной библиотеке Ruby опасный метод подразумевает, что если имя метода заканчивается восклицательным знаком (!), то, в отличие от соответствующего метода без восклицательного знака, он изменяет свой объект-получатель безвозвратно. Почти всегда в стандартной библиотеке Ruby каждому методу с восклицательным знаком (имя метода заканчивается на !), который изменяет объект-получатель, соответствует вариант без восклицательного знака (имя метода НЕ заканчивается на !), который не изменяет объект-получатель. Это соглашение обычно соблюдается в стандартной библиотеке Ruby, но может не соблюдаться в других библиотеках Ruby.

По соглашению методы, оканчивающиеся вопросительным знаком, возвращают логическое значение, но они не всегда возвращают только true или false. Часто они возвращают объект, обозначающий истинное значение (так называемое «truthy»-значение).

Методы, оканчивающиеся знаком равенства, обозначают методы присваивания.

class C
  def attr
    @attr
  end

  def attr=(val)
    @attr = val
  end
end

c = C.new
c.attr      #=> nil
c.attr = 10 # calls "attr=(10)"
c.attr      #=> 10

Методы присваивания нельзя определять с помощью сокращённого синтаксиса.

Ниже приведены имена методов для различных операторов Ruby. Каждый из этих операторов принимает только один аргумент. После оператора указано его типичное назначение или название. Придание оператору альтернативного смысла может привести к путанице: пользователь ожидает, что плюс будет складывать, минус — вычитать и т. д. Кроме того, нельзя изменить приоритет операторов.

+

сложение

-

вычитание

*

умножение

**

возведение в степень

/

деление

%

деление по модулю, String#%

&

И

^

исключающее ИЛИ

>>

сдвиг вправо

<<

сдвиг влево, добавление

==

равно

!=

не равно

===

равенство в операторе case. См. Object#===

=~

сопоставление с шаблоном (не только с регулярными выражениями)

!~

не соответствует шаблону

<=>

сравнение, также известное как оператор «космический корабль». См. Comparable

<

меньше

<=

меньше или равно

>

больше

>=

больше или равно

Чтобы определить унарные методы минуса и плюса, поставьте перед оператором @, как в +@:

class C
  def -@
    puts "you inverted this object"
  end
end

obj = C.new

-obj # prints "you inverted this object"

Символ @ необходим, чтобы отличать унарные операторы минуса и плюса от бинарных операторов минуса и плюса.

Перед @ также можно поставить тильду и отрицание (!) в унарных методах, но это необязательно, поскольку бинарных операторов тильды и отрицания нет.

Унарные методы принимают ноль аргументов.

Кроме того, можно определять методы получения и присваивания элементов: соответственно [] и []=. Оба метода могут принимать один или несколько аргументов, а метод получения элемента может не принимать аргументов.

class C
  def [](a, b)
    puts a + b
  end

  def []=(a, b, c)
    puts a * b + c
  end
end

obj = C.new

obj[2, 3]     # prints "5"
obj[2, 3] = 4 # prints "10"

Возвращаемые значения

По умолчанию метод возвращает последнее выражение, вычисленное в его теле. В примере выше последним (и единственным) вычисленным выражением была простая сумма 1 + 1. Ключевое слово return можно использовать, чтобы явно указать, что метод возвращает значение.

def one_plus_one
  return 1 + 1
end

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

def two_plus_two
  return 2 + 2
  1 + 1  # this expression is never evaluated
end

Обратите внимание: при использовании синтаксиса присваивания возвращаемое значение методов присваивания игнорируется. Вместо него возвращается аргумент:

def a=(value)
  return 1 + value
end

p(self.a = 5) # prints 5

Фактическое возвращаемое значение будет получено при прямом вызове метода:

p send(:a=, 5) # prints 6

Область видимости

Стандартный синтаксис определения метода:

def my_method
  # ...
end

добавляет метод в класс. С помощью ключевого слова class можно определить метод экземпляра для конкретного класса:

class C
  def my_method
    # ...
  end
end

Метод можно определить и для другого объекта. «Метод класса» (метод, определённый для класса, а не для его экземпляра) можно определить следующим образом:

class C
  def self.my_method
    # ...
  end
end

Однако это лишь частный случай более широких возможностей синтаксиса Ruby — возможности добавлять методы к любому объекту. Классы являются объектами, поэтому добавление методов класса — это просто добавление методов к объекту Class.

Синтаксис добавления метода к объекту выглядит так:

greeting = "Hello"

def greeting.broaden
  self + ", world!"
end

greeting.broaden # returns "Hello, world!"

self — это ключевое слово, обозначающее текущий объект, рассматриваемый компилятором. Это может прояснить использование self при определении метода класса выше. Действительно, пример добавления метода hello к классу String можно переписать так:

def String.hello
  "Hello, world!"
end

Определённый таким образом метод называется «синглтон-методом». broaden будет существовать только у экземпляра строки greeting. У других строк не будет broaden.

Переопределение

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

>> "43".to_i
=> 43
>> class String
>>   def to_i
>>     42
>>   end
>> end
=> nil
>> "43".to_i
=> 42

Это фактически нарушит работу любого кода, который использует метод String#to_i для разбора чисел из строк.

Аргументы

Метод может принимать аргументы. Список аргументов следует за именем метода:

def add_one(value)
  value + 1
end

При вызове пользователь метода add_one должен передать аргумент. В теле метода аргумент является локальной переменной. Затем метод прибавит к этому аргументу единицу и вернёт результат. Если передать 1, этот метод вернёт 2.

Скобки вокруг аргументов необязательны:

def add_one value
  value + 1
end

В сокращённых определениях методов скобки обязательны:

# OK
def add_one(value) = value + 1
# SyntaxError
def add_one value = value + 1

Несколько аргументов разделяются запятой:

def add_values(a, b)
  a + b
end

При вызове аргументы необходимо передавать в строго определённом порядке. Иными словами, аргументы являются позиционными.

Значения по умолчанию

Для аргументов можно задавать значения по умолчанию:

def add_values(a, b = 1)
  a + b
end

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

def add_values(a = 1, b = 2, c)
  a + b + c
end

Этот вариант вызовет SyntaxError:

def add_values(a = 1, b, c = 1)
  a + b + c
end

Значения аргументов по умолчанию могут ссылаться на аргументы, уже вычисленные как локальные переменные; значения аргументов всегда вычисляются слева направо. Поэтому следующий вариант допустим:

def add_values(a = 1, b = a)
  a + b
end
add_values
# => 2

А этот вариант вызовет NameError (если только не определён метод с именем b):

def add_values(a = b, b = 1)
  a + b
end
add_values
# NameError (undefined local variable or method `b' for main:Object)

Array: разложение

Можно разложить (распаковать или извлечь значения из) Array, добавив дополнительные скобки в списке аргументов:

def my_method((a, b))
  p a: a, b: b
end

my_method([1, 2])

Будет выведено:

{:a=>1, :b=>2}

Если в Array аргумента есть дополнительные элементы, они будут проигнорированы:

def my_method((a, b))
  p a: a, b: b
end

my_method([1, 2, 3])

Результат будет таким же, как выше.

Чтобы собрать оставшиеся аргументы, можно использовать *. Это разделит Array на первый элемент и остальные элементы:

def my_method((a, *b))
  p a: a, b: b
end

my_method([1, 2, 3])

Будет выведено:

{:a=>1, :b=>[2, 3]}

Аргумент будет разложен, если он отвечает на вызов to_ary. Определяйте to_ary только в том случае, если ваш объект можно использовать вместо Array.

Внутренние скобки используют только один из переданных аргументов. Если аргумент не является Array, он будет присвоен первому аргументу при разложении, а оставшиеся аргументы при разложении будут nil:

def my_method(a, (b, c), d)
  p a: a, b: b, c: c, d: d
end

my_method(1, 2, 3)

Будет выведено:

{:a=>1, :b=>2, :c=>nil, :d=>3}

Разложение можно вкладывать на любую глубину:

def my_method(((a, b), c))
  # ...
end

Аргумент-массив/хеш

Если поставить перед аргументом *, все оставшиеся аргументы будут преобразованы в массив:

def gather_arguments(*arguments)
  p arguments
end

gather_arguments 1, 2, 3 # prints [1, 2, 3]

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

Собирать аргументы можно в начале или в середине списка:

def gather_arguments(first_arg, *middle_arguments, last_arg)
  p middle_arguments
end

gather_arguments 1, 2, 3, 4 # prints [2, 3]

Аргумент-массив захватит Hash как последний элемент, если вызывающий код передал именованные аргументы после всех позиционных аргументов.

def gather_arguments(*arguments)
  p arguments
end

gather_arguments 1, a: 2 # prints [1, {:a=>2}]

Однако это происходит только в том случае, если метод не объявляет никаких именованных аргументов.

def gather_arguments_keyword(*positional, keyword: nil)
 p positional: positional, keyword: keyword
end

gather_arguments_keyword 1, 2, three: 3
#=> raises: unknown keyword: three (ArgumentError)

Также обратите внимание: для игнорирования аргументов можно использовать * без имени:

def ignore_arguments(*)
end

При вызове метода также можно использовать * без имени, чтобы напрямую передать аргументы другому методу:

def delegate_arguments(*)
  other_method(*)
end

Именованные аргументы

Именованные аргументы похожи на позиционные аргументы со значениями по умолчанию:

def add_values(first: 1, second: 2)
  first + second
end

Произвольные именованные аргументы принимаются с помощью **:

def gather_arguments(first: nil, **rest)
  p first, rest
end

gather_arguments first: 1, second: 2, third: 3
# prints 1 then {:second=>2, :third=>3}

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

Чтобы сделать конкретный именованный аргумент обязательным, не указывайте для него значение по умолчанию:

def add_values(first:, second:)
  first + second
end
add_values
# ArgumentError (missing keywords: first, second)
add_values(first: 1, second: 2)
# => 3

При сочетании именованных и позиционных аргументов все позиционные аргументы должны указываться перед именованными.

Также обратите внимание: для игнорирования именованных аргументов можно использовать **:

def ignore_keywords(**)
end

При вызове метода также можно использовать **, чтобы передать именованные аргументы другому методу:

def delegate_keywords(**)
  other_method(**)
end

Чтобы пометить метод как принимающий именованные аргументы, но фактически не принимать их, можно использовать **nil:

def no_keywords(**nil)
end

Вызов такого метода с именованными аргументами или непустой распаковкой именованных аргументов приведёт к ошибке ArgumentError. Этот синтаксис поддерживается, чтобы позднее можно было добавить в метод именованные аргументы, не нарушая обратную совместимость.

Если определение метода не принимает именованные аргументы и не используется синтаксис **nil, любые именованные аргументы при вызове метода будут преобразованы в позиционный аргумент типа Hash:

def meth(arg)
  arg
end
meth(a: 1)
# => {:a=>1}

Аргумент-блок

Аргумент-блок обозначается с помощью & и должен идти последним:

def my_method(&my_block)
  my_block.call(self)
end

Чаще всего аргумент-блок используется для передачи блока другому методу:

def each_item(&block)
  @items.each(&block)
end

Блоку не обязательно задавать имя, если вы просто передаёте его другому методу:

def each_item(&)
  @items.each(&)
end

Если вы собираетесь только вызвать блок и не будете иным образом работать с ним или передавать его другому методу, предпочтительно использовать yield без явного параметра блока. Этот метод эквивалентен первому методу в этом разделе:

def my_method
  yield self
end

Передача аргументов

Начиная с Ruby 2.7 доступен синтаксис передачи всех аргументов:

def concrete_method(*positional_args, **keyword_args, &block)
  [positional_args, keyword_args, block]
end

def forwarding_method(...)
  concrete_method(...)
end

forwarding_method(1, b: 2) { puts 3 }
#=>  [[1], {:b=>2}, #<Proc:...skip...>]

Вызов с передачей аргументов ... доступен только в методах, определённых с помощью ....

def regular_method(arg, **kwarg)
  concrete_method(...) # Syntax error
end

Начиная с Ruby 3.0, перед ... в определениях и вызовах можно указывать начальные аргументы (в определениях это могут быть только позиционные аргументы без значений по умолчанию).

def request(method, path, **headers)
  puts "#{method.upcase} #{path} #{headers}"
end

def get(...)
  request(:GET, ...) # leading argument in invoking
end

get('http://ruby-lang.org', 'Accept' => 'text/html')
# Prints: GET http://ruby-lang.org {"Accept"=>"text/html"}

def logged_get(msg, ...) # leading argument in definition
  puts "Invoking #get: #{msg}"
  get(...)
end

logged_get('Ruby site', 'http://ruby-lang.org')
# Prints:
#   Invoking #get: Ruby site
#   GET http://ruby-lang.org {}

Обратите внимание: отсутствие скобок при вызовах с передачей аргументов может привести к неожиданным результатам:

def log(...)
  puts ...  # This would be treated as `puts()...',
            # i.e. endless range from puts result
end

log("test")
# Prints: warning: ... at EOL, should be parenthesized?
# ...and then empty line

Exception: обработка

В методах предусмотрен неявный блок обработки исключений, поэтому для обработки исключений не нужно использовать begin или end. Этот код:

def my_method
  begin
    # code that may raise an exception
  rescue
    # handle exception
  end
end

можно записать так:

def my_method
  # code that may raise an exception
rescue
  # handle exception
end

Аналогично, если вы хотите, чтобы код выполнялся всегда, даже если возникло исключение, можно использовать ensure без begin и end:

def my_method
  # code that may raise an exception
ensure
  # code that runs even if previous code raised an exception
end

Также можно сочетать rescue с ensure и/или else, не используя begin и end:

def my_method
  # code that may raise an exception
rescue
  # handle exception
else
  # only run if no exception raised above
ensure
  # code that runs even if previous code raised an exception
end

Если нужно перехватить исключение только в части метода, используйте begin и end. Подробности см. на странице об обработке исключений.

Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API