5

What type of exception should I throw?

I have a console class which describes rectangle of cells which a user can index by passing in a coordinate:

width, height = 80, 25
console = Console.new(width, height)

x, y = 2, 5
point_on_screen = Point.new(x, y)
console[point_on_screen] = 'a'

I am now wondering what types of exception to throw when an index occurs out of bounds . That is, outside of the (constant!) domains x:[0, width] and y:[0, height]. I could throw a RangeError or an ArgumentError *.

  1. Does it matter which type of exception to throw as long as the error message is useful?
  2. Should I use the same exception type for both the construction of the console and the indexing? Are the two really semantically different?

* returning a kind of out of bounds object is possibly a better option - something like a Null Object Pattern - but not what I am wondering about here.

ArgumentError

One option is to throw an ArgumentError because the argument is obviously wrong, dimensions can never be non-negative. This is what I throw in example 1, because we ought to know in advance that the dimensions should be non-negative.

Example 1:

console = Console new(-3, 4) # Argument error

RangeError

Another option is to throw a RangeError because the argument is out of the valid range. I use these for indexing. The argument is always out of range if x < 0, but if x ≥ 0 then it depends on the width of the console if the x is in the domain.

Example 2:

console = Console.new(20, 40)
pt = Point.new(-3, 4)
Console[pt] # RangeError
200_success
  • 1,568
  • 11
  • 20

2 Answers2

5

Does it matter which type of exception to throw as long as the error message is useful?

In cases like this, I often defer to the official docs for definitions and usage examples.

The docs for RangeError are a bit terse:

RangeError
Raised when a given numerical value is out of range.

[1, 2, 3].drop(1 << 100)
raises the exception:
RangeError: bignum too big to convert into long

And although the docs for ArgumentError imply encouragement towards a more specific Exception, one of the examples (in bold below) certainly seems like a potential match for your use case:

ArgumentError
Raised when the arguments are wrong and there isn't a more specific Exception class.

Ex: passing the wrong number of arguments
[1, 2, 3].first(4, 5)
raises the exception:
ArgumentError: wrong number of arguments (2 for 1)

Ex: passing an argument that is not acceptable:
[1, 2, 3].first(-4)
raises the exception:
ArgumentError: negative array size

Generally speaking, Argument-type exceptions are a clear and expected way to convey to the caller that they've supplied some invalid parameters.

Regarding your mention of "... as long as the message is helpful...", I say yes, absolutely. Always try to include a meaningful message when you raise an error.

Regarding the null object alternative: Personally, I'd encourage you to explicitly raise the error rather than return a null object (or some other out-of-bounds indicator). Code built with this pattern is plagued by constant null checks, and inevitably a null object will escape your safety nets and wreak unexpected havoc on your program.

When I'm working with software that routinely returns nulls or special values as a "something went wrong" indicator, I need to spend additional mental cycles to make sure I'm checking all responses, and that I'm looking for the right error flags. (But, convention is certainly a factor, so if you're working with an existing code base which uses the null object pattern, it might be best to continue.)

Here's some good additional reading about exceptions vs. error variables: Are error variables an anti-pattern or good design?

  • Thanks for the link and the extra pointer on the null object. I was also worried that sometimes it might be better to have an immediate exception thrown, rather than silently working with the null object, although I suppose I could have done some logging... – R. Peereboom Jul 10 '14 at 17:06
0

You could always create a custom exception if you dont find the exceptions you have at your disposal fit correctly.

class MyUsefulException < Exception
end

raise MyUsefulException, 'Huzzah, my exception lives!!!'
Sean
  • 101
  • 1
  • Custom errors should derive from StandardError or something derived from that. Exception is a low level class that user code should not directly inherit. – thomthom Dec 09 '16 at 12:00
  • @thomthom that was more just psuedo-code placeholder, where `Exception` could be any of the classes in the `Exception` Hierarchy tree. I am in agreement with you that you should inherit from `StandardError` in most cases. – Sean Dec 09 '16 at 18:47