Thoughts on adding additional ivars to select exception types to expose additional information about the exception? For example, TypeCastError, only exposes the exception message, such as cast from Nil to String failed. However, there isn't a way to know typed failed to cast to what type.
It would be nice to be able to do something like:
require "json"
def convert(type : String.class, data)
data.as_s
rescue ex : TypeCastError
raise "Expected #{type} but got #{ex.from_type}"
end
convert String, JSON.parse("null")
Currently the only way to do this is to parse the type from the message. However, this is assuming the specific instance of TypeCastError has that data in the message in the first place.
Granted, not all exceptions would need this. I could see like IndexError or KeyError including a reference to the index/key that caused the exception.
File and Socket errors should carry a reference to which IO::FileDescriptor/Socket instance the error occurred on, too.
Alright cool. Here's what I'm thinking:
IndexError#index - Index that caused the errorKeyError#key - Key that caused the errorTypeCastError#from_type - Actual type of the value being casted#to_type - Expected type after castingIO::Error#io - The related IO object (could be a file/normal io depending on the context)How's that sound?
Some questions:
KeyError, TypeCastError, and IndexErrorWe could provide those things as strings. Why do you need the type? Those exceptions signal bugs in the code, they are not meant to be rescued.
What鈥檚 the use case? In your example you just re-raised another, less specific, exception.
About the IO keep in mind that many times the one that raises the exception is wrapped around other layers so it鈥檚 not easy to use it properly during error handling. And handling and re-raising everywhere is just too costly. I wonder why I didn鈥檛 see such property in analogous exceptions of other languages.
Interestingly, Go's errors.Is and errors.As are neat ways to "find" an exception in an exception which has a cause. It'd be possible to provide something similar in crystal for extracting an IO::Error from any exception which wraps it.
We could provide those things as strings.
I'd probably be okay with this.
What鈥檚 the use case? In your example you just re-raised another, less specific, exception.
I could see a use for logging, as it would allow running aggregations against the values versus just the string.
Of course, that was just an example. In reality I would be using it to pull out the actual type provided to reraise another exception with context that the original exception doesn't know about. Something like:
ParseException.new self.format, "Could not parse #{type} from #{data} (#{ex.from_type})."
Most helpful comment
File and Socket errors should carry a reference to which
IO::FileDescriptor/Socketinstance the error occurred on, too.