log icon indicating copy to clipboard operation
log copied to clipboard

Feature Request: downcasting `Log`, but for real

Open 1e1001 opened this issue 11 months ago • 2 comments

Seems #399 was already closed (4.5 years ago, for bad usecase & maybe backwards compatibility reasons?), but I have an actual use!

My logger writes a message on panic, but does so with a custom format different to anything you can write with the Log trait. So I need to be able to downcast the &dyn Log into my own logger from the panic handler. I could probably use a OnceLock to store if my logger's been initialized but log already does that with the (set_)logger methods, so it feels redundant.

For now my current way of doing this is this very cursed set of functions:

fn as_dyn_ref(logger: *const Logger) -> *const dyn Log {
  // split into one function to always attach the same metadata
  logger as *const dyn Log
}
fn upcast_log(logger: &'static Logger) -> &'static dyn Log {
  // SAFETY: as_dyn_ref returns a reference to the same object as passed in
  unsafe { &*as_dyn_ref(logger) }
}
fn downcast_log(log: &'static dyn Log) -> Option<&'static Logger> {
  // horribly cursed implementation to fetch a reference to the installed logger
  let (logger_ptr, logger_meta) = (&raw const *log).to_raw_parts();
  let (_, fake_logger_meta) = as_dyn_ref(ptr::null::<Logger>()).to_raw_parts();
  (logger_meta == fake_logger_meta).then(|| {
    // SAFETY: v-tables match so it's probably ours!
    unsafe { &*logger_ptr.cast::<Logger>() }
  })
}

but that's a lot of unsafe, and nightly-only.

To prevent needing to add a + 'static bound on Log (which probably breaks compatibility somehow), yandros suggested (in rplcs #dark-arts) adding something like:

fn type_id(&self) -> TypeId
  where Self: 'static
{
  TypeId::of<Self>()
}

as an automatic implementation on the Log trait.

1e1001 avatar Feb 06 '25 11:02 1e1001

Hi @1e1001 :wave:

This is something I'd be open to exploring a bit more. We'd need to be careful adding a type_id method to the Log trait, because it would be unsound to override. The Error trait for instance defines its squirrel-type-id method like this to try get around that

KodrAus avatar May 14 '25 21:05 KodrAus

Ah yeah that makes sense, in which case I'm not quite sure how one would implement that without some sort of + 'static or + Any bound on the Log trait itself (although I'm curious on how much real code that would actually break, given that the library only ever asks for &'static dyn Logs).

1e1001 avatar May 21 '25 16:05 1e1001