Методы
Методы реализуют функциональность вашей программы. Вот простое определение метода:
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.