rust-lang-deprecated / failure

Error management
https://rust-lang-nursery.github.io/failure/
Apache License 2.0
1.42k stars 138 forks source link

Ideal representation of Error #51

Open withoutboats opened 6 years ago

withoutboats commented 6 years ago

In theory, error could:

  1. Be 1 pointer in size.
  2. Never allocate if backtraces are turned off & the failure is a ZST.

To accomplish this goal, error would look like this:

struct Error {
     inner: usize,
}

This usize would be a pointer to a heap allocation or a vtable. Which kind of pointer it is would be stored using the extra bits of space that alignment guarantees give us on each platform.

The heap allocation would have this layout:

struct Inner {
    vtable: &'static (),
    backtrace: Backtrace,
    data: Fail, // A DST
}

I'm not sure its even possible to write this with unsafe code on stable Rust.

cc @cramertj @dtolnay #9 #20.

cramertj commented 6 years ago

AFAIK, you'll need double-boxing if you want to store unsized data with a single-width pointer. Presumably you're getting the size to dealloc from vtable? I don't believe there's a user-facing way to free an object of a dynamic size, but @dtolnay would know better.

You could still do struct Inner { backtrace: Backtrace, data: Box<Fail> }.

withoutboats commented 6 years ago

AFAIK, you'll need double-boxing if you want to store unsized data with a single-width pointer.

Aren't we essentially going to be leaking the data (because of the bitmasking) and then reconstructing the address of it every time? We can make the second pointer null (and then swap it with the vtable pointer)?

Stuff about how to free the object is a good question, we would probably want to store the size inline.

withoutboats commented 6 years ago

Basically what we're talking about doing is reimplementing dynamic dispatch in unsafe code instead of using real trait objects.

withoutboats commented 6 years ago

This isn't really related but its also about doing unsafe vtable shenanigans:

it would be great to be able to follow the std::error::Error::cause chain to its conclusion instead of just bottoming out at the first non-Fail error we come to. We could do that if we could manufacture a fake vtable somehow.

The Fail vtable has two methods: backtrace (which in the fake one would use the default implementation) and cause. cause is the tricky method. We'd have to call the std::Error::Error::cause method from the original vtable and then perform this very same vtable swap on the result.

No idea how to implement this even if it were possible on stable today.