frafferz/geek

# This is a comment

Method in the Madness: Ensure and Return

Was pondering the question: what code runs when method level rescue, else and ensure are used in ruby?

TL;DR summary

1
2
3
4
5
6
7
8
9
def some_method
  # main body
rescue
  # rescue code
else
  # alternative to rescue
ensure
  # always run me last
end
  1. Without return the last computed value that is not in the ensure block is returned (this will either be the main body, the rescue block or the else block).
  2. Using return in the main body of the method means that else block doesn’t run.
  3. Using return in an ensure block always overrides any other value returned by the method, regardless of whether any other section of the method also used the return keyword.
  4. Values from an ensure block are only ever returned when the return keyword is used.

Simple function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def value(sym)
  puts("ran #{sym}")
  sym
end

def fn1
  value(:fn1)
rescue
  value(:rescue1)
else
  value(:else1)
ensure
  value(:ensure1)
end

fn1() #=> :else1

Output:

ran fn1
ran else1
ran ensure1

Function with error:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def value(sym)
  puts("ran #{sym}")
  sym
end

def fn2
  raise value(:fn2)
rescue
  value(:rescue2)
else
  value(:else2)
ensure
  value(:ensure2)
end

fn2() #=> :rescue2

Output:

ran fn2
ran rescue2
ran ensure2

Function with return in main body

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def value(sym)
  puts("ran #{sym}")
  sym
end

def fn3
  return value(:fn3)
rescue
  value(:rescue3)
else
  value(:else3)
ensure
  value(:ensure3)
end

fn3() #=> :fn3

Output:

ran fn3
ran ensure3

Function with return in main body and return in ensure block

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def value(sym)
  puts("ran #{sym}")
  sym
end

def fn4
  return value(:fn4)
rescue
  return value(:rescue4)
else
  return value(:else4)
ensure
  return value(:ensure4)
end

fn4() #=> :ensure4

Output:

ran fn4
ran ensure4

Function with return in main body and in ensure and error raised

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def value(sym)
  puts("ran #{sym}")
  sym
end

def fn5
  return raise value(:fn5)
rescue
  return value(:rescue5)
else
  return value(:else5)
ensure
  return value(:ensure5)
end

fn5() #=> :ensure5

Output:

ran fn5
ran rescue5
ran ensure5