Feature Request: downcasting Log, but for real #666

Open
opened 2025-02-06 05:24:50 -06:00 by 1e1001 · 3 comments
1e1001 commented 2025-02-06 05:24:50 -06:00 (Migrated from github.com)

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.

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: ```rs 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](https://discord.com/channels/273534239310479360/592856094527848449/1336662380997902396)) adding something like: ```rs fn type_id(&self) -> TypeId where Self: 'static { TypeId::of<Self>() } ``` as an automatic implementation on the `Log` trait.
KodrAus commented 2025-05-14 16:47:12 -05:00 (Migrated from github.com)

Hi @1e1001 👋

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

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](https://github.com/rust-lang/rust/blob/master/library/core/src/error.rs#L109-L121)
1e1001 commented 2025-05-21 11:06:48 -05:00 (Migrated from github.com)

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).

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 Log`s).
djugei commented 2026-09-19 02:35:58 -05:00 (Migrated from github.com)

I am the author of indicatif-log-bridge and i would like the same feature, downcasting the &'static dyn Log to LogWrapper (the specific logger) to access the interal MultiProgress.

i might be misinformed but i think this would be as simple as adding a simple function with a default implementation to Log

trait Log {
 //...
  fn as_any(&'static self) -> &'static dyn Any {
    self
  }
}

due to the default implementation it would be backwards compatible.

I am the author of indicatif-log-bridge and i would like the same feature, downcasting the `&'static dyn Log` to LogWrapper (the specific logger) to access the interal `MultiProgress`. i might be misinformed but i think this would be as simple as adding a simple function with a default implementation to Log ``` trait Log { //... fn as_any(&'static self) -> &'static dyn Any { self } } ``` due to the default implementation it would be backwards compatible.
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-rust-lang/log#666
No description provided.