-
Notifications
You must be signed in to change notification settings - Fork 1
Operators
Most of the operators in Ruby have behaviour defined by the class of the object they are applied to, when the class implements the operator's method.
Specifically:
- For unary operators, the operator's method is invoked on the operand e.g.
!ais equivalent toa.!() - For binary operators, the operator's method is invoked on the left operand with a single argument of the right operand, e.g.
a + bis equivalent toa.+(b)
Because operator behaviour is defined by API, not language, I won't try to describe them exhaustively here, but I will give an overview of their behaviour for core classes.
- <value>
Implements <value>.-@()
Standard unary minus operator.
| Class | Action |
|---|---|
| Numeric | Returns 0 - <value>
|
| Fixnum | Returns negation of <value>
|
| Float | Returns negation of <value>
|
| Bignum | Returns negation of <value>
|
| Complex | Returns negation of <value>
|
+ <value>
Implements <value>.+@()
Standard unary plus operator.
| Class | Action |
|---|---|
| Numeric | Returns <value>
|
~ <value>
Implements <value>.~()
For integers, a standard complement operator. For regular expressions, performs a match to the last line.
| Class | Action |
|---|---|
| Fixnum | Returns one's complement of <value>
|
| Bignum | Returns one's complement of <value> (bignums are conceptually of infinite length) |
| Regexp | Returns the default implementation of <value> =~ $_
|
! <value>
not <value>
Implements <value>.!()
A standard logical "not" operator.
| Class | Action |
|---|---|
| Object | Returns true if <value> is falsy, otherwise false |
<left-value> + <right-value>
Implements <left-value>.+(<right_value>)
Standard addition operator for Numerics. Standard concatenation operator for Arrays and Strings. Add seconds for Time.
| Class | Action |
|---|---|
| Fixnum, Bignum, Rational, Float, Complex | Returns the addition of left and right Numeric values. Can add 2 values each of any Numeric class, and it will "upgrade" one of the operands to match the class of the other (in the order listed here) |
| Array | Returns the concatenation of the left and right Array values. Right operand can be implicitly converted with to_ary
|
| String | Returns the concatenation of the left and right String values. Right operand can be implicitly converted with to_str
|
| Time | Returns a Time object resulting in adding the Numeric right value, in seconds, to the Time object |
<left-value> - <right-value>
Implements <left-value>.-(<right_value>)
Standard subtraction operator for Numerics. For Arrays, returns an Array containing the left value's items excluding the right value's items. For Time, subtract another Time and get # seconds, or subtract # seconds and get a Time.
| Class | Action |
|---|---|
| Fixnum, Bignum, Rational, Float, Complex | Returns the subtraction of left and right Numeric values. Can subtract 2 values each of any Numeric class, and it will "upgrade" one of the operands to match the class of the other (in the order listed here) |
| Array | Returns an Array containing the values in the left operand excluding the values in the right operand. Matching elements are located by converting one Array to a Hash and testing each element in the other Array against it. Right operand can be implicitly converted with to_ary
|
| Time | If right value is Numeric, returns a Time object resulting in subtracting that value, in seconds, from the Time object. If right value is Time, returns the number of seconds between the 2 times. |
<left-value> * <right-value>
Implements <left-value>.*(<right_value>)
Standard multiplication operator for Numerics. For Arrays and Strings, concatenates the specified number of copies of the left operand. For arrays can also be used to join elements into a string separated by right operand.
| Class | Action |
|---|---|
| Fixnum, Bignum, Rational, Float, Complex | Returns the multiplication of left and right Numeric values. Can multiply 2 values each of any Numeric class, and it will "upgrade" one of the operands to match the class of the other (in the order listed here) |
| Array | If right value is a String, will return a String containing each of the Array elements (stringified) separated by the string in the right value. If right value is a Numeric, will return an Array that is the concatenation of that many copies of the left value. |
| String | Right value is a Numeric, returns a String that is the concatenation of that many copies of the left value. |
<left-value> / <right-value>
Implements <left-value>./(<right_value>)
Standard division operator for Numerics.
| Class | Action |
|---|---|
| Fixnum, Bignum, Rational, Float, Complex | Returns the division of left and right Numeric values. Can divide 2 values each of any Numeric class, and it will "upgrade" one of the operands to match the class of the other (in the order listed here) |
<left-value> % <right-value>
Implements <left-value>.%(<right_value>)
Standard modulus operator for Numerics (except Complex). For Strings, acts like sprintf with the left value being the format string and the right value being arguments.
| Class | Action |
|---|---|
| Fixnum, Bignum, Rational, Float | Returns the modulus of left and right Numeric values. Can modulo 2 values each of any Numeric class, and it will "upgrade" one of the operands to match the class of the other (in the order listed here) |
| String | Returns sprintf(<left-value>, *<right-value>)
|
<left-value> ** <right-value>
Implements <left-value>.**(<right_value>)
Standard power operator for Numerics.
| Class | Action |
|---|---|
| Fixnum, Bignum, Rational, Float, Complex | Returns the power of left and right Numeric values |
<left-value> & <right-value>
Implements <left-value>.&(<right_value>)
Standard bitwise and operator for Integers. For true/false/nil, behaves as logical and except without short circuit evaluation and always returning true or false. For Arrays, behaves as intersection operator.
| Class | Action |
|---|---|
| Fixnum, Bignum | Returns the bitwise and of left and right Integer values, upgrading a Fixnum to a Bignum if necessary |
true, false, nil
|
Returns <left-value> && <right-value> except will always evaluate both arguments and return true or false
|
| Array | Returns an Array containing the values in the left operand excluding any values not in the right operand. Matching elements are located by converting one Array to a Hash and testing each element in the other Array against it. Right operand can be implicitly converted with to_ary
|
<left-value> | <right-value>
Implements <left-value>.|(<right_value>)
Standard bitwise or operator for Integers. For true/false/nil, behaves as logical or except without short circuit evaluation and always returning true or false. For Arrays, behaves as union operator.
| Class | Action |
|---|---|
| Fixnum, Bignum | Returns the bitwise or of left and right Integer values, upgrading a Fixnum to a Bignum if necessary |
true, false, nil
|
Returns ` |
| Array | Returns an Array containing the values in the left operand with values in the right operand appended except for values already in the left operand. Matching elements are located by converting one Array to a Hash and testing each element in the other Array against it. Right operand can be implicitly converted with to_ary
|
<left-value> ^ <right-value>
Implements <left-value>.^(<right_value>)
Standard bitwise exclusive or operator for Integers. For true/false/nil, behaves as logical exclusive or except without short circuit evaluation and always returning true or false.
| Class | Action |
|---|---|
| Fixnum, Bignum | Returns the bitwise xor of left and right Integer values, upgrading a Fixnum to a Bignum if necessary |
true, false, nil
|
Returns true if either <left-value> or <right-value> is truthy, but not both, otherwise returns false, again without short circuit evaluation |
<left-value> << <right-value>
Implements <left-value>.<<(<right_value>)
Standard bitwise left shift operator for Integers. For Arrays, modifies the left value by appending the right value, and returns the left value. For Strings, concatenates the right value (either a String or an Integer codepoint) onto the left value and returns the left value.
| Class | Action |
|---|---|
| Fixnum, Bignum | Returns the left value bitwise left-shifted by the right Integer value, upgrading to a Bignum if necessary. If right value is negative, treated as right shift |
| Array | Returns the left value after appending the right value to it |
| String | Concatenates the right value (either a String or an Integer codepoint) onto the left value and returns the left value |
<left-value> >> <right-value>
Implements <left-value>.>>(<right_value>)
Standard bitwise right shift operator for Integers.
| Class | Action |
|---|---|
| Fixnum, Bignum | Returns the left value bitwise right-shifted by the right value. If right value is negative, treated as left shift |
<left-value> =~ <right-value>
Implements <left-value>.=~(<right-value>)
For Regexp, implements a regular expression match, returning the match position. For String and Symbol, tries the match with the operands switched around. For Kernel (i.e. default implementation), returns nil.
| Class | Action |
|---|---|
| Kernel | returns nil
|
| Regexp | Performs a regular expression match to right value, returns nil if failed, otherwise the position in the right value where the match was found (beginning from 0) |
| String, Symbol | If the right value is a String, raise error. If it's a Regexp, return the default implementation of <right-value> =~ <left-value>, otherwise return the current implementation of <right-value> =~ <left-value>
|
<left-value> !~ <right-value>
Implements <left-value>.!~(<right-value>)
Tests for whether <left-value> =~ <right-value> fails.
| Class | Action |
|---|---|
| Kernel | returns false if <left-value> =~ <right-value> is truthy, otherwise true
|
<left-value> > <right-value>
Implements <left-value>.>(<right-value>)
<left-value> < <right-value>
Implements <left-value>.<(<right-value>)
<left-value> >= <right-value>
Implements <left-value>.>=(<right-value>)
<left-value> <= <right-value>
Implements <left-value>.<=(<right-value>)
<left-value> <=> <right-value>
Implements <left-value>.<=>(<right-value>)
<left-value> == <right-value>
Implements <left-value>.==(<right-value>)
<left-value> != <right-value>
Implements <left-value>.!=(<right-value>)
<left-value> === <right-value>
Implements <left-value>.===(<right-value>)
Syntactically speaking, Array references are not really operators, e.g. they don't really have operands or precedence in the usual sense. However, they are syntax elements which are class-defined, so we will describe their standard implementations here.
<obj>[<args>]
Implements <obj>.[](<args>)
<obj>[<args>] = <value>
Implements <obj>.[]=(<args>, <value>)
The following operators are defined by the language - their behaviour can't be altered.
<lhs> = <value>
<lhs> = <asgn_value> rescue <resc_value>
Returns: The value assigned
Same as other languages - assigns a value to a variable. <lhs> can be:
- a local variable
- an instance variable
- a class variable
- a global variable
- an array reference
- an attribute reference
- a constant reference
Note that a local variable in <lhs> which did not previously exist will be created by this expression. That same local variable can actually be used as part of the <value> expression - its value in such usage will be nil.
In some circumstances a rescue modifier may be attached to the assignment expression, whereas normally they can only be applied to whole statements. This results in the value of <asgn_value> rescue <resc_value> being assigned to the variable. See the operator precedence section for rules on when this is permitted.
<lhs> = <mrhs>
Returns: the value of <mrhs> as encapsulated in an Array
<mrhs> is a comma-separated list of regular and splat call arguments, and is processed similar to a method call. The resulting list of values is put in an Array, and this value is assigned to the <lhs>. In other words, this syntax is equivalent to assigning an array literal with <lhs> = [ <mrhs> ] except that the <mrhs> syntax does not permit hash associations or trailing commas.
<mlhs> = <value>
<mlhs> = <mrhs>
Returns: the value of <value> or <mrhs>
Multiple comma-separated variable references are provided in <mlhs>, and the value to be assigned is treated as an Array, its contents assigned to variable references in <mlhs>. Any variable references permitted in a simple assignments are also permitted in multiple left-hand-side assignments.
<mlhs> is a comma-separated list of at least 1 of the following elements, in order:
- 0 or more nested "mandatory" variable references, which may be (in any order):
- Simple variable references
- Nested multiple left-hand-side structures enclosed in
(and) - An optional "rest" variable reference, which may be:
- A regular "rest" variable reference, which is written as a variable reference prefixed by
*(e.g.*rest) - An anonymous "rest" reference, which is written as
* - If a "rest" variable reference is present
- 0 or more "post" variable references, which have the same form as nested "mandatory" variable references
Nested multiple left-hand-side structures have the same form as <mlhs>.
Some additional syntax rules apply:
- If
<mlhs>or a nested multiple left-hand-side structure contains only "mandatory" references, it may be followed by a trailing, - If there is only a single "mandatory" reference, it MUST be followed by a trailing
, -
<mlhs>(excluding nested structures) may optionally be enclosed in(and), without changing the meaning of the expression (i.e. without it being considered a nested expression). Note that this means thata, b = cand(a, b) = cmean the same thing, however((a, b)) = cis different.
A value is assigned to <mlhs> as follows:
- Convert the value to Array if necessary:
- If the value is already an Array or an instance of a subclass of Array, OK
- Otherwise, conversion to an Array is attempted by calling the
to_arymethod on the value- If the
to_arymethod call fails to resolve or returnsnil, the conversion has failed - If the returned object from
to_aryis not a subclass of Array, an exception is thrown - Otherwise, OK
- If the
- If the conversion failed (without throwing an exception), then the conversion is effected by creating a new Array of 1 element, which is the value
- The contents of the array are assigned to:
- "mandatory" variable references, from left to right, then
- if a "rest" reference is present, any values in the array in excess of the number of mandatory/post references are selected and:
- if the "rest" reference is anonymous, ignored
- otherwise, compiled into an array and assigned to the "rest" reference
- "post" variable references, from left to right
- if there are fewer elements in the array than there are mandatory/post variable references, the unset references are set to
nil - Any remaining values in the array are ignored
- If there is a regular "rest" variable reference and no array elements to assign to it, it is assigned an empty array
- If there are nested multiple left-hand-side structures in mandatory or post, this array conversion and assignment process is repeated for them
Some examples:
a, b = [1, 2, 3] # a = 1, b = 2
a, *, (b, c) = [1, 2, 3, [4, 5]] # a = 1, b = 4, c = 5
<lhs> <op>= <value>
Returns: the value of after the expression is processed
There are complex assignment forms for all the binary arithmetic operators, formed by writing an = on the end of the operator e.g. +=. For these operators, an expression a <op>= b is equivalent to a = a <op> b, e.g. a += b is equivalent to a = a + b.
All complex assignments may not have multiple left- or right-hand-sides, and they may not have explicitly-scoped constant references on their left-hand-side.
<lhs> &&= <value>
<lhs> ||= <value>
Returns: the value of after the expression is processed
Mostly, these are similar to other complex assignment operators (i.e. a &&= b is like a = a && b), except:
- If the expression is short circuited and
<value>is never evaluated, then the assignment is not performed at all, which has implications for assignments that may have side effects, especially: - global variable assignments may cause trace procedures to be invoked
- constant assignments may assign to a different constant than the one that is accessed
- attribute and array reference assignments invoke methods to perform the assignment, which may have whatever side effects they like
- In some cases, the
||=expression has the ability to determine that<lhs>is falsy when it's not defined at all - so it assigns to the<lhs>when other complex assignments would throw an exception
Specific behaviour for a &&= b:
- If
ais other than a local variable, equivalent toa && (a = b), i.e. doesn't assign ifais falsy - If
ais a local variable, it is equivalent toa && (a = b)except that ifawas not defined before the expression,a &&= bdefines it and therefore doesn't cause a syntax error (whereasa && (a = b)would cause a syntax error). Since assigning to local variables has no side effects, you might say this form is equivalent toa = a && beven though an assignment is not performed ifais falsy
Specific behaviour for a ||= b:
- If
ais an array or attribute reference, equivalent toa || (a = b), i.e. doesn't assign ifais truthy - If
ais a local variable, it is equivalent toa || (a = b)except that ifawas not defined before the expression,a ||= bdefines it and therefore doesn't cause a syntax error (whereasa || (a = b)would cause a syntax error). Since assigning to local variables has no side effects, you might say this form is equivalent toa = a || beven though an assignment is not performed ifais truthy - If
ais a global variable, instance variable, class variable, or (implicitly namespaced) constant, it will also test to see if the referenced variable is defined and, if not, perform the assignment i.e. it's equivalent to(defined? a && a) || (a = b). For undefined global variables, this appears to make no difference as they returnnilanyway, but for the others it prevents an exception being thrown due to the variables being undefined
<value> && <value>
<value> and <value>
Returns: the left value, if falsy, otherwise the right value
Similar to other languages, in that it returns a truthy value if both operands are truthy, and a falsy value otherwise. Uses short-circuit evaluation - the second operand is not evaluated if the first is falsy. Typically used to test whether two values are both truthy, however can also be used to evaluate the second value on the condition that the first one is truthy.
a && b therefore behaves like:
(
tmp = a
if tmp
b
else
tmp
end
)
Both &&, and forms of the operator have the same meaning, however they have different positions in the syntax grammar and precedence.
<value> || <value>
<value> or <value>
Returns: the left value, if truthy, otherwise the right value
Similar to other languages, in that returns a truthy value if one of the operands is truthy, and a falsy value otherwise. Uses short-circuit evaluation - the second operand is not evaluated if the first is truthy. Typically used to test whether at least one of two values is truthy, however can also be used to evaluate the second value on the condition that the first one is falsy.
a || b therefore behaves like:
(
tmp = a
unless tmp
b
else
tmp
end
)
Both ||, or forms of the operator have the same meaning, however they have different positions in the syntax grammar and precedence.
<cond> ? <value-if-true> : <value-if-false>
Returns: see if expression
Standard ternary operator - equivalent to if <cond> then <value-if-true> else <value-if-false> end, including the special condition processing that applies to if expressions
defined? <expr>
Returns: nil if undefined, otherwise a String (see below)
The defined? operator is used to test whether <expr> is defined. <expr> is not evaluated, its structure is examined at runtime to determine whether it is defined. If not, it returns nil, otherwise it returns a String. The behaviour of defined? based on the <expr> that is passed to it is described below:
| <expr> | Is defined if | Return value |
|---|---|---|
nil |
Always | "nil" |
self |
Always | "self" |
true |
Always | "true" |
false |
Always | "false" |
| An array literal | If contains other than simple (non splat/hash) elements, or every element within is defined? (short-circuiting on the first failure) |
"expression" |
| A local variable | Always (a local variable which is not defined is a method call) | "local-variable" |
| An instance variable | The variable is found | "instance-variable" |
| A class variable | The variable is found | "class-variable" |
$&, $` , $', $", backreferences |
The variable is not nil
|
"global-variable" |
| An other global variable | The variable exists (except for where it has been referenced but never assigned to) | "global-variable" |
| An implicitly-namespaced constant | The constant is defined according to constant-defined logic | "constant" |
| An explicitly-namespaced constant | The namespace is defined? and (after evaluating it) the constant is defined on it according to constant-defined logic |
"constant" |
| Any type of method call with a block | Always | "expression" |
| An implicitly-targeted method call | If the method is defined for self, it isn't flagged "not implemented", and if all its arguments are mandatory, they are all defined (short-circuiting on the first failure) |
"method" |
An array reference on self
|
As above for the array reference method | "method" |
A simple assignment to an array/attribute reference on self
|
As above for the array/attribute set method | "method" |
| An explicitly-targeted method call | If the target is defined and, after evaluating it, the method is defined for it and its visibility makes it accessible from the current scope, or respond_to_missing?(:method, false) on the target indicates it responds, and if all its arguments are mandatory, they are all defined (short-circuiting on the first failure) |
"method" |
An array reference on other than self
|
As above for the array reference method | "method" |
A simple assignment to an array/attribute reference on other than self
|
As above for the array/attribute set method | "method" |
| A class-defined operator expression | As above for the operator method | "method" |
A yield expression |
If there is a block passed to the current method | "yield" |
A super expression |
If the super call would resolve according to normal logic, except no method_missing, "not implemented" methods are counted undefined, and buggy interactions with define_method
|
"super" |
| Any other assignment expression | Always | "assignment" |
| Any other expression | Always | "expression" |
Note that in some circumstances defined? evaluates part of its expression. If this evaluation raises an exception, defined? will catch this exception and return nil.
<from-value> .. <to-value>
<from-value> ... <to-value>
These operators serve 2 different functions.
Returns: a Range object
These operators are normally Range constructors, and are equivalent to the default implementations of:
- For
..,Range.new(<from-value>, <to-value>, false) - For
...,Range.new(<from-value>, <to-value>, true)
Returns: the flip-flop's state
If these operators appear (in some circumstances) within the conditional expression of if, elsif, unless, while, until, ?:, not and !, they are flip-flops, not range constructors.
Flip-flops create a special hidden variable in the local variable context, which is used to keep track of whether the flip-flop is "on" or "off" - it is this state which is returned as the value of the flip-flop. A flip-flop starts its life "off", and will be "on" from the point when the <from-value> expression is truthy up to and including the point where the <to-value> is truthy. The difference between the two forms is that for ..., the <to-value> expression will not be evaluated when the <from-value> first becomes truthy i.e. if both values become truthy at the same time, this will not cause the flip-flop to be switched "off" after once being reported "on".
If either value is an Fixnum integer literal, it is equivalent to comparing via == to $. i.e. 3 is equivalent to 3 == $..
A flip-flop is not a Ruby value and can't be manipulated as such - they are purely a feature of conditional expressions.
Here is some Ruby code describing the behaviour of flip-flops, assuming tmp is a local context variable.
changed = false
unless tmp # flip-flop is off
if from_value
tmp = true
changed = true
end
end
if tmp && !(changed && operator == "...") # flip-flop is on, but don't try to switch off if it was just switched on and operator is "..."
if to_value
tmp = false
changed = true
end
end
(!tmp && changed) ? true : tmp # report "on" up to and including the moment it's switched "off"
Broadly, the following table describes the precedence of Ruby operators.
No|Type/Associativity|Description|Operators
---|---|---|---|---
1|Binary left|Reference|. ::
2|Unary|General|! ~ +
3|Binary right|Power|**
4|Unary|Minus|-
5|Binary left|Products|* / %
6|Binary left|Additions|+ -
7|Binary left|Shifts|<< >>
8|Binary left|Bitwise and|&
9|Binary left|Bitwise or|| ^
10|Binary left (TODO why?)|Directional comparisons|> >= < <=
11|Binary none|General comparisons|<=> == === != =~ !~
12|Binary left|Logical and, argument form|&&
13|Binary left|Logical or, argument form|||
14|Binary left|Range constructors|.. ...
15|Ternary right|Logical ternary|?:
16|Unary|Defined|defined?
17|Binary none|Assignment modifier|rescue
18|Binary right|Assignments|= += -= etc.
19|Unary|Logical not, expression form|not
20|Binary left|Logical and/or, expression form|and or
21|Binary left|Statement modifiers|if unless while until rescue
Some special rules also apply:
- If a rescue modifier is applied to an assignment, e.g.
<lhs> = <rhs> rescue <value>,rescuebecomes an assignment modifier and takes precedence over the assignment operator (i.e. it will be interpreted as<lhs> = ( <rhs> rescue <value> )) according to the following rules: - The assignment operator may be simple or complex
- This does not apply to an assignment with a multiple left-hand-side, a multiple right-hand-side or a command call on the right-hand-side
- If the argument to
defined?ornot?is within parentheses and there is no whitespace preceding it, then the operator has the highest precedence (higher even than.and::)
A Ruby Language Reference
Copyright © by Michael Hore, 2016.
Introduction to This Document
Ruby Elements
- Classes and Modules
- Methods
- Blocks, Procs and Lambdas
- Execution Context and Closures
- Variables, Constants and Namespaces
- Types and Literals
- Ruby Expressions
- Operators
Syntax Grammar
Exceptions and Throw
Ruby Sourcefiles and Libraries
Multi Threading
Execution and Lifecycle