mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
f53dfab95c
This allows for the following syntax: ```ruby def foo(*) bar(*) end def baz(**) quux(**) end ``` This is a natural addition after the introduction of anonymous block forwarding. Anonymous rest and keyword rest arguments were already supported in method parameters, this just allows them to be used as arguments to other methods. The same advantages of anonymous block forwarding apply to rest and keyword rest argument forwarding. This has some minor changes to #parameters output. Now, instead of `[:rest], [:keyrest]`, you get `[:rest, :*], [:keyrest, :**]`. These were already used for `...` forwarding, so I think it makes it more consistent to include them in other cases. If we want to use `[:rest], [:keyrest]` in both cases, that is also possible. I don't think the previous behavior of `[:rest], [:keyrest]` in the non-... case and `[:rest, :*], [:keyrest, :**]` in the ... case makes sense, but if we did want that behavior, we'll have to make more substantial changes, such as using a different ID in the ... forwarding case. Implements [Feature #18351]
652 lines
17 KiB
Text
652 lines
17 KiB
Text
= Methods
|
|
|
|
Methods implement the functionality of your program. Here is a simple method
|
|
definition:
|
|
|
|
def one_plus_one
|
|
1 + 1
|
|
end
|
|
|
|
A method definition consists of the +def+ keyword, a method name, the body of
|
|
the method, +return+ value and the +end+ keyword. When called the method will
|
|
execute the body of the method. This method returns +2+.
|
|
|
|
Since Ruby 3.0, there is also a shorthand syntax for methods consisting
|
|
of exactly one expression:
|
|
|
|
def one_plus_one = 1 + 1
|
|
|
|
This section only covers defining methods. See also the {syntax documentation
|
|
on calling methods}[rdoc-ref:syntax/calling_methods.rdoc].
|
|
|
|
== Method Names
|
|
|
|
Method names may be one of the operators or must start a letter or a character
|
|
with the eighth bit set. It may contain letters, numbers, an <code>_</code>
|
|
(underscore or low line) or a character with the eighth bit set. The convention
|
|
is to use underscores to separate words in a multiword method name:
|
|
|
|
def method_name
|
|
puts "use underscores to separate words"
|
|
end
|
|
|
|
Ruby programs must be written in a US-ASCII-compatible character set such as
|
|
UTF-8, ISO-8859-1 etc. In such character sets if the eighth bit is set it
|
|
indicates an extended character. Ruby allows method names and other identifiers
|
|
to contain such characters. Ruby programs cannot contain some characters like
|
|
ASCII NUL (<code>\x00</code>).
|
|
|
|
The following are examples of valid Ruby methods:
|
|
|
|
def hello
|
|
"hello"
|
|
end
|
|
|
|
def こんにちは
|
|
puts "means hello in Japanese"
|
|
end
|
|
|
|
Typically method names are US-ASCII compatible since the keys to type them
|
|
exist on all keyboards.
|
|
|
|
Method names may end with a <code>!</code> (bang or exclamation mark), a
|
|
<code>?</code> (question mark), or <code>=</code> (equals sign).
|
|
|
|
The bang methods (<code>!</code> at the end of the method name) are called and
|
|
executed just like any other method. However, by convention, a method with an
|
|
exclamation point or bang is considered dangerous. In Ruby's core library the
|
|
dangerous method implies that when a method ends with a bang (<code>!</code>),
|
|
it indicates that unlike its non-bang equivalent, permanently modifies its
|
|
receiver. Almost always, the Ruby core library will have a non-bang
|
|
counterpart (method name which does NOT end with <code>!</code>) of every bang
|
|
method (method name which does end with <code>!</code>) that does not modify
|
|
the receiver. This convention is typically true for the Ruby core library but
|
|
may or may not hold true for other Ruby libraries.
|
|
|
|
Methods that end with a question mark by convention return boolean, but they
|
|
may not always return just +true+ or +false+. Often, they will return an
|
|
object to indicate a true value (or "truthy" value).
|
|
|
|
Methods that end with an equals sign indicate an assignment method.
|
|
|
|
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
|
|
|
|
Assignment methods can not be defined using the shorthand syntax.
|
|
|
|
These are method names for the various Ruby operators. Each of these
|
|
operators accepts only one argument. Following the operator is the typical
|
|
use or name of the operator. Creating an alternate meaning for the operator
|
|
may lead to confusion as the user expects plus to add things, minus to
|
|
subtract things, etc. Additionally, you cannot alter the precedence of the
|
|
operators.
|
|
|
|
<code>+</code> :: add
|
|
<code>-</code> :: subtract
|
|
<code>*</code> :: multiply
|
|
<code>**</code> :: power
|
|
<code>/</code> :: divide
|
|
<code>%</code> :: modulus division, String#%
|
|
<code>&</code> :: AND
|
|
<code>^</code> :: XOR (exclusive OR)
|
|
<code>>></code> :: right-shift
|
|
<code><<</code> :: left-shift, append
|
|
<code>==</code> :: equal
|
|
<code>!=</code> :: not equal
|
|
<code>===</code> :: case equality. See Object#===
|
|
<code>=~</code> :: pattern match. (Not just for regular expressions)
|
|
<code>!~</code> :: does not match
|
|
<code><=></code> :: comparison aka spaceship operator. See Comparable
|
|
<code><</code> :: less-than
|
|
<code><=</code> :: less-than or equal
|
|
<code>></code> :: greater-than
|
|
<code>>=</code> :: greater-than or equal
|
|
|
|
To define unary methods minus and plus, follow the operator with an
|
|
<code>@</code> as in <code>+@</code>:
|
|
|
|
class C
|
|
def -@
|
|
puts "you inverted this object"
|
|
end
|
|
end
|
|
|
|
obj = C.new
|
|
|
|
-obj # prints "you inverted this object"
|
|
|
|
The <code>@</code> is needed to differentiate unary minus and plus
|
|
operators from binary minus and plus operators.
|
|
|
|
You can also follow tilde and not (<code>!</code>) unary methods with
|
|
<code>@</code>, but it is not required as there are no binary tilde
|
|
and not operators.
|
|
|
|
Unary methods accept zero arguments.
|
|
|
|
Additionally, methods for element reference and assignment may be defined:
|
|
<code>[]</code> and <code>[]=</code> respectively. Both can take one or more
|
|
arguments, and element reference can take none.
|
|
|
|
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"
|
|
|
|
== Return Values
|
|
|
|
By default, a method returns the last expression that was evaluated in the body
|
|
of the method. In the example above, the last (and only) expression evaluated
|
|
was the simple sum <code>1 + 1</code>. The +return+ keyword can be used to
|
|
make it explicit that a method returns a value.
|
|
|
|
def one_plus_one
|
|
return 1 + 1
|
|
end
|
|
|
|
It can also be used to make a method return before the last expression is
|
|
evaluated.
|
|
|
|
def two_plus_two
|
|
return 2 + 2
|
|
1 + 1 # this expression is never evaluated
|
|
end
|
|
|
|
Note that for assignment methods the return value will be ignored when using
|
|
the assignment syntax. Instead, the argument will be returned:
|
|
|
|
def a=(value)
|
|
return 1 + value
|
|
end
|
|
|
|
p(self.a = 5) # prints 5
|
|
|
|
The actual return value will be returned when invoking the method directly:
|
|
|
|
p send(:a=, 5) # prints 6
|
|
|
|
== Scope
|
|
|
|
The standard syntax to define a method:
|
|
|
|
def my_method
|
|
# ...
|
|
end
|
|
|
|
adds the method to a class. You can define an instance method on a specific
|
|
class with the +class+ keyword:
|
|
|
|
class C
|
|
def my_method
|
|
# ...
|
|
end
|
|
end
|
|
|
|
A method may be defined on another object. You may define a "class method" (a
|
|
method that is defined on the class, not an instance of the class) like this:
|
|
|
|
class C
|
|
def self.my_method
|
|
# ...
|
|
end
|
|
end
|
|
|
|
However, this is simply a special case of a greater syntactical power in Ruby,
|
|
the ability to add methods to any object. Classes are objects, so adding
|
|
class methods is simply adding methods to the Class object.
|
|
|
|
The syntax for adding a method to an object is as follows:
|
|
|
|
greeting = "Hello"
|
|
|
|
def greeting.broaden
|
|
self + ", world!"
|
|
end
|
|
|
|
greeting.broaden # returns "Hello, world!"
|
|
|
|
+self+ is a keyword referring to the current object under consideration
|
|
by the compiler, which might make the use of +self+ in defining a class
|
|
method above a little clearer. Indeed, the example of adding a +hello+
|
|
method to the class +String+ can be rewritten thus:
|
|
|
|
def String.hello
|
|
"Hello, world!"
|
|
end
|
|
|
|
A method defined like this is called a "singleton method". +broaden+ will only
|
|
exist on the string instance +greeting+. Other strings will not have +broaden+.
|
|
|
|
== Overriding
|
|
|
|
When Ruby encounters the +def+ keyword, it doesn't consider it an error if the
|
|
method already exists: it simply redefines it. This is called
|
|
_overriding_. Rather like extending core classes, this is a potentially
|
|
dangerous ability, and should be used sparingly because it can cause unexpected
|
|
results. For example, consider this irb session:
|
|
|
|
>> "43".to_i
|
|
=> 43
|
|
>> class String
|
|
>> def to_i
|
|
>> 42
|
|
>> end
|
|
>> end
|
|
=> nil
|
|
>> "43".to_i
|
|
=> 42
|
|
|
|
This will effectively sabotage any code which makes use of the method
|
|
<code>String#to_i</code> to parse numbers from strings.
|
|
|
|
== Arguments
|
|
|
|
A method may accept arguments. The argument list follows the method name:
|
|
|
|
def add_one(value)
|
|
value + 1
|
|
end
|
|
|
|
When called, the user of the +add_one+ method must provide an argument. The
|
|
argument is a local variable in the method body. The method will then add one
|
|
to this argument and return the value. If given +1+ this method will
|
|
return +2+.
|
|
|
|
The parentheses around the arguments are optional:
|
|
|
|
def add_one value
|
|
value + 1
|
|
end
|
|
|
|
The parentheses are mandatory in shorthand method definitions:
|
|
|
|
# OK
|
|
def add_one(value) = value + 1
|
|
# SyntaxError
|
|
def add_one value = value + 1
|
|
|
|
Multiple arguments are separated by a comma:
|
|
|
|
def add_values(a, b)
|
|
a + b
|
|
end
|
|
|
|
When called, the arguments must be provided in the exact order. In other
|
|
words, the arguments are positional.
|
|
|
|
=== Default Values
|
|
|
|
Arguments may have default values:
|
|
|
|
def add_values(a, b = 1)
|
|
a + b
|
|
end
|
|
|
|
The default value does not need to appear first, but arguments with defaults
|
|
must be grouped together. This is ok:
|
|
|
|
def add_values(a = 1, b = 2, c)
|
|
a + b + c
|
|
end
|
|
|
|
This will raise a SyntaxError:
|
|
|
|
def add_values(a = 1, b, c = 1)
|
|
a + b + c
|
|
end
|
|
|
|
Default argument values can refer to arguments that have already been
|
|
evaluated as local variables, and argument values are always evaluated
|
|
left to right. So this is allowed:
|
|
|
|
def add_values(a = 1, b = a)
|
|
a + b
|
|
end
|
|
add_values
|
|
# => 2
|
|
|
|
But this will raise a +NameError+ (unless there is a method named
|
|
+b+ defined):
|
|
|
|
def add_values(a = b, b = 1)
|
|
a + b
|
|
end
|
|
add_values
|
|
# NameError (undefined local variable or method `b' for main:Object)
|
|
|
|
=== Array Decomposition
|
|
|
|
You can decompose (unpack or extract values from) an Array using extra
|
|
parentheses in the arguments:
|
|
|
|
def my_method((a, b))
|
|
p a: a, b: b
|
|
end
|
|
|
|
my_method([1, 2])
|
|
|
|
This prints:
|
|
|
|
{:a=>1, :b=>2}
|
|
|
|
If the argument has extra elements in the Array they will be ignored:
|
|
|
|
def my_method((a, b))
|
|
p a: a, b: b
|
|
end
|
|
|
|
my_method([1, 2, 3])
|
|
|
|
This has the same output as above.
|
|
|
|
You can use a <code>*</code> to collect the remaining arguments. This splits
|
|
an Array into a first element and the rest:
|
|
|
|
def my_method((a, *b))
|
|
p a: a, b: b
|
|
end
|
|
|
|
my_method([1, 2, 3])
|
|
|
|
This prints:
|
|
|
|
{:a=>1, :b=>[2, 3]}
|
|
|
|
The argument will be decomposed if it responds to #to_ary. You should only
|
|
define #to_ary if you can use your object in place of an Array.
|
|
|
|
Use of the inner parentheses only uses one of the sent arguments. If the
|
|
argument is not an Array it will be assigned to the first argument in the
|
|
decomposition and the remaining arguments in the decomposition will be +nil+:
|
|
|
|
def my_method(a, (b, c), d)
|
|
p a: a, b: b, c: c, d: d
|
|
end
|
|
|
|
my_method(1, 2, 3)
|
|
|
|
This prints:
|
|
|
|
{:a=>1, :b=>2, :c=>nil, :d=>3}
|
|
|
|
You can nest decomposition arbitrarily:
|
|
|
|
def my_method(((a, b), c))
|
|
# ...
|
|
end
|
|
|
|
=== Array/Hash Argument
|
|
|
|
Prefixing an argument with <code>*</code> causes any remaining arguments to be
|
|
converted to an Array:
|
|
|
|
def gather_arguments(*arguments)
|
|
p arguments
|
|
end
|
|
|
|
gather_arguments 1, 2, 3 # prints [1, 2, 3]
|
|
|
|
The array argument must appear before any keyword arguments.
|
|
|
|
It is possible to gather arguments at the beginning or in the middle:
|
|
|
|
def gather_arguments(first_arg, *middle_arguments, last_arg)
|
|
p middle_arguments
|
|
end
|
|
|
|
gather_arguments 1, 2, 3, 4 # prints [2, 3]
|
|
|
|
The array argument will capture a Hash as the last entry if keywords were
|
|
provided by the caller after all positional arguments.
|
|
|
|
def gather_arguments(*arguments)
|
|
p arguments
|
|
end
|
|
|
|
gather_arguments 1, a: 2 # prints [1, {:a=>2}]
|
|
|
|
However, this only occurs if the method does not declare any keyword arguments.
|
|
|
|
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)
|
|
|
|
Also, note that a bare <code>*</code> can be used to ignore arguments:
|
|
|
|
def ignore_arguments(*)
|
|
end
|
|
|
|
You can also use a bare <code>*</code> when calling a method to pass the
|
|
arguments directly to another method:
|
|
|
|
def delegate_arguments(*)
|
|
other_method(*)
|
|
end
|
|
|
|
=== Keyword Arguments
|
|
|
|
Keyword arguments are similar to positional arguments with default values:
|
|
|
|
def add_values(first: 1, second: 2)
|
|
first + second
|
|
end
|
|
|
|
Arbitrary keyword arguments will be accepted with <code>**</code>:
|
|
|
|
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}
|
|
|
|
When calling a method with keyword arguments the arguments may appear in any
|
|
order. If an unknown keyword argument is sent by the caller, and the method
|
|
does not accept arbitrary keyword arguments, an ArgumentError is raised.
|
|
|
|
To require a specific keyword argument, do not include a default value
|
|
for the keyword argument:
|
|
|
|
def add_values(first:, second:)
|
|
first + second
|
|
end
|
|
add_values
|
|
# ArgumentError (missing keywords: first, second)
|
|
add_values(first: 1, second: 2)
|
|
# => 3
|
|
|
|
When mixing keyword arguments and positional arguments, all positional
|
|
arguments must appear before any keyword arguments.
|
|
|
|
Also, note that <code>**</code> can be used to ignore keyword arguments:
|
|
|
|
def ignore_keywords(**)
|
|
end
|
|
|
|
You can also use <code>**</code> when calling a method to delegate
|
|
keyword arguments to another method:
|
|
|
|
def delegate_keywords(**)
|
|
other_method(**)
|
|
end
|
|
|
|
To mark a method as accepting keywords, but not actually accepting
|
|
keywords, you can use the <code>**nil</code>:
|
|
|
|
def no_keywords(**nil)
|
|
end
|
|
|
|
Calling such a method with keywords or a non-empty keyword splat will
|
|
result in an ArgumentError. This syntax is supported so that keywords
|
|
can be added to the method later without affected backwards compatibility.
|
|
|
|
If a method definition does not accept any keywords, and the
|
|
<code>**nil</code> syntax is not used, any keywords provided when calling
|
|
the method will be converted to a Hash positional argument:
|
|
|
|
def meth(arg)
|
|
arg
|
|
end
|
|
meth(a: 1)
|
|
# => {:a=>1}
|
|
|
|
=== Block Argument
|
|
|
|
The block argument is indicated by <code>&</code> and must come last:
|
|
|
|
def my_method(&my_block)
|
|
my_block.call(self)
|
|
end
|
|
|
|
Most frequently the block argument is used to pass a block to another method:
|
|
|
|
def each_item(&block)
|
|
@items.each(&block)
|
|
end
|
|
|
|
You are not required to give a name to the block if you will just be passing
|
|
it to another method:
|
|
|
|
def each_item(&)
|
|
@items.each(&)
|
|
end
|
|
|
|
If you are only going to call the block and will not otherwise manipulate it
|
|
or send it to another method, using <code>yield</code> without an explicit
|
|
block parameter is preferred. This method is equivalent to the first method
|
|
in this section:
|
|
|
|
def my_method
|
|
yield self
|
|
end
|
|
|
|
=== Argument Forwarding
|
|
|
|
Since Ruby 2.7, an all-arguments forwarding syntax is available:
|
|
|
|
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...>]
|
|
|
|
Calling with forwarding <code>...</code> is available only in methods
|
|
defined with <code>...</code>.
|
|
|
|
def regular_method(arg, **kwarg)
|
|
concrete_method(...) # Syntax error
|
|
end
|
|
|
|
Since Ruby 3.0, there can be leading arguments before <code>...</code>
|
|
both in definitions and in invocations (but in definitions they can be
|
|
only positional arguments without default values).
|
|
|
|
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 {}
|
|
|
|
Note that omitting parentheses in forwarding calls may lead to
|
|
unexpected results:
|
|
|
|
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 Handling
|
|
|
|
Methods have an implied exception handling block so you do not need to use
|
|
+begin+ or +end+ to handle exceptions. This:
|
|
|
|
def my_method
|
|
begin
|
|
# code that may raise an exception
|
|
rescue
|
|
# handle exception
|
|
end
|
|
end
|
|
|
|
May be written as:
|
|
|
|
def my_method
|
|
# code that may raise an exception
|
|
rescue
|
|
# handle exception
|
|
end
|
|
|
|
Similarly, if you wish to always run code even if an exception is raised,
|
|
you can use +ensure+ without +begin+ and +end+:
|
|
|
|
def my_method
|
|
# code that may raise an exception
|
|
ensure
|
|
# code that runs even if previous code raised an exception
|
|
end
|
|
|
|
You can also combine +rescue+ with +ensure+ and/or +else+, without
|
|
+begin+ and +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
|
|
|
|
If you wish to rescue an exception for only part of your method, use +begin+ and
|
|
+end+. For more details see the page on {exception
|
|
handling}[rdoc-ref:syntax/exceptions.rdoc].
|