Ruby has a fatal
exception, but there is no guidance on how to raise
it and I cannot figure it out. How do I raise a fatal
exception in Ruby?
Sure you can.
Try this
FatalError = ObjectSpace.each_object(Class).find { |klass| klass < Exception && klass.inspect == 'fatal' }
And then
raise FatalError.new("famous last words")
How does this work?
fatal
is an internal class without associated top-level constantObjectSpace.each_object(Class)
enumerates over all classesfind { ... }
finds an exception class named "fatal"
NB though, despite its name fatal
is not special, it can be rescued. If you are looking for a way to end your program maybe best call the global exit
method?
begin
raise FatalError.new
rescue Exception => e
puts "Not so fatal after all..."
end
ObjectSpace
trick. 10h too late! Please rename each
and you'll get my vote ;) –
Nambypamby fatal
doesn't seem special. It doesn't kill the process, and can still be rescued. –
Nambypamby fatal
is not special, it can be rescued. I don't see a reason why someone should ban this from production code. –
Standice fatal
is a Ruby-internal Exception and should be used that way. For your own exceptions, you should use your own Exception classes, which almost always should inherit from StandardError
in order to be rescuable by a simple rescue
. Just because you can doesn't mean you should. –
Whig fatal
isn't special, just a subclass of Exception
, but raising it in the way as above is like calling rb_raise(rb_eFatal, ...)
, not rb_fatal(...)
, you can see they use different tags here, the last cannot be rescued –
Wiggly Short answer is, you can, but probably shouldn't. This exception is reserved for Ruby internals. It is effectively hidden to users by being a constant with an all lowercase identifier. (Ruby won't do a constant lookup unless the identifier starts with an uppercase character.)
fatal
NameError: undefined local variable or method `fatal' for main:Object
The same is true when using Object#const_get
:
Object.const_get(:fatal)
NameError: wrong constant name fatal
If this exception class was intended for us to use, then it would be readily available, and not hidden away.
#include <ruby.h>
and rb_fatal("%s", msg);
afterwards. –
Fanning ObjectSpace
to get the class. –
Standice class A; end; a = A.new; A = 12345; [a.class.name, A] # => ["A", 12345]
–
Standice For MRI, you can reproduce the way fatal
is defined via ffi
and convert C API VALUEs with fiddle
, but note that simply raising it raise fatal, '...'
is like calling rb_raise(rb_eFatal, ...)
, not rb_fatal(...)
, you can see they use different tags here, the last cannot be rescued (rescue fatal
won't work too)
require 'ffi'
require 'fiddle'
module RbFFI
extend FFI::Library
ffi_lib FFI::CURRENT_PROCESS
end
RbFFI.attach_function :rb_define_class, [:string, :ulong], :ulong
RbFFI.attach_function :rb_raise, [:ulong, :string, :varargs], :void
RbFFI.attach_function :rb_fatal, [:string, :varargs], :void
rb_eException = Fiddle.dlwrap Exception # Just a hacky way to convert get VALUE representation of Ruby object
rb_eFatal = RbFFI.rb_define_class('fatal', rb_eException)
fatal = Fiddle.dlunwrap(rb_eFatal) # Just a hacky way to convert get Ruby object from VALUE representation
at_exit do
puts '-----------------'
puts 'at_exit'
end
def test(name, &block)
puts '-----------------'
puts "test: #{name}"
begin
block.call
rescue Exception => e
puts "rescued #{e.message} (#{e.class})"
ensure
puts "ensure: #{name}"
end
puts "done: #{name}"
end
test('1') { RbFFI.rb_raise rb_eFatal, 'rb_raise rb_eFatal' }
test('2') { raise fatal, 'raise fatal' }
test('3') { RbFFI.rb_fatal 'rb_fatal' }
produces
-----------------
test: 1
rescued rb_raise rb_eFatal (fatal)
ensure: 1
done: 1
-----------------
test: 2
rescued raise fatal (fatal)
ensure: 2
done: 2
-----------------
test: 3
ensure: 3
-----------------
at_exit
/path/to/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/ffi-1.16.3/lib/ffi/variadic.rb:47:in `invoke': rb_fatal (fatal)
from /path/to/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/ffi-1.16.3/lib/ffi/variadic.rb:47:in `call'
from /path/to/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/ffi-1.16.3/lib/ffi/variadic.rb:62:in `rb_fatal'
from fatal.rb:38:in `block in <main>'
from fatal.rb:26:in `test'
from fatal.rb:38:in `<main>'
© 2022 - 2024 — McMap. All rights reserved.
Process.kill('KILL', $$)
to shoot your own process with an unblockable signal. This is usually a really bad idea, mind you, as you probably want to let whateverensure
blocks are defined kick in properly. – Chaensure
blocks are to be executed. – Fanningrb_bug
is an example of an immediate and complete halt: "Terminates the interpreter immediately. This function should be called under the situation caused by the bug in the interpreter. No exception handling nor ensure execution will be done." A hardkill
is similar. – Cha