Skip to content

Conversation

@xobs
Copy link
Contributor

@xobs xobs commented Nov 25, 2025

Description

Add support for reporting errors that conform to core::error::Error. This allows for calling e.source(), which may provide more information. For example, probe-rs uses error sources to create chains of errors, and without this errors are very terse.

Before:

During the execution of GDB an error was encountered:
Target threw a fatal error: A RISC-V specific error occurred.
 (No further information is available)

After:

During the execution of GDB an error was encountered:
Target threw a fatal error: A RISC-V specific error occurred.
   ... caused by: Error occurred during execution of an abstract command: Exception

This is gated by "std", so should not pose any API changes to deeply embedded targets.

API Stability

  • This PR does not require a breaking API change

Checklist

  • Documentation
    • Ensured any public-facing rustdoc formatting looks good (via cargo doc)
    • (if appropriate) Added feature to "Debugging Features" in README.md
  • Validation
    • Included output of running examples/armv4t with RUST_LOG=trace + any relevant GDB output under the "Validation" section below
    • Included output of running ./example_no_std/check_size.sh before/after changes under the "Validation" section below
  • If implementing a new protocol extension IDET
    • Included a basic sample implementation in examples/armv4t
    • IDET can be optimized out (confirmed via ./example_no_std/check_size.sh)
    • OR implementation requires introducing non-optional binary bloat (please elaborate under "Description")
  • If upstreaming an Arch implementation
    • I have tested this code in my project, and to the best of my knowledge, it is working as intended.

Validation

GDB output
(gdb) info mem
Using memory regions provided by the target.
Num Enb Low Addr   High Addr  Attrs
0   y   0x08000000 0x08010000 flash blocksize 0x4000 nocache
1   y   0x08010000 0x08020000 flash blocksize 0x10000 nocache
2   y   0x08020000 0x08080000 flash blocksize 0x20000 nocache
3   y   0x20000000 0x20020000 rw nocache
(gdb)
armv4t output
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/examples/armv4t`
loading section ".text" into memory from [0x55550000..0x55550078]
Setting PC to 0x55550000
Waiting for a GDB connection on "127.0.0.1:9001"...
Debugger connected from 127.0.0.1:51333
 TRACE gdbstub::protocol::recv_packet > <-- +
 TRACE gdbstub::protocol::recv_packet > <-- $qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;QThreadOptions+;no-resumed+;memory-tagging+;xmlRegisters=i386;error-message+#14
 TRACE gdbstub::protocol::response_writer > --> $PacketSize=1000;vContSupported+;multiprocess+;QStartNoAckMode+;ReverseContinue+;ReverseStep+;QDisableRandomization+;QEnvironmentHexEncoded+;QEnvironmentUnset+;QEnvironmentReset+;QStartupWithShell+;QSetWorkingDir+;swbreak+;hwbreak+;QTBuffer:size+;TracepointSource+;QCatchSyscalls+;qXfer:features:read+;qXfer:memory-map:read+;qXfer:exec-file:read+;qXfer:auxv:read+;qXfer:libraries-svr4:read+#39
 TRACE gdbstub::protocol::recv_packet     > <-- +
 TRACE gdbstub::protocol::recv_packet     > <-- $vCont?#49
 TRACE gdbstub::protocol::response_writer > --> $vCont;c;C;s;S;r#0f
 TRACE gdbstub::protocol::recv_packet     > <-- +
 TRACE gdbstub::protocol::recv_packet     > <-- $vMustReplyEmpty#3a
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("vMustReplyEmpty")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- +
 TRACE gdbstub::protocol::recv_packet     > <-- $QStartNoAckMode#b0
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- +
 TRACE gdbstub::protocol::recv_packet     > <-- $!#21
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- $Hgp0.0#ad
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- $qXfer:features:read:target.xml:0,ffb#79
 TRACE gdbstub::protocol::response_writer > --> $m<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target version="1.0">
    <architecture>armv4t</architecture>
    <feature name="org.gnu.gdb.arm.core">
        <vector id="padding" type="uint32" count="25"/>

        <reg name="r0" bitsize="32" type="uint32"/>
        <reg name="r1" bitsize="32" type="uint32"/>
        <reg name="r2" bitsize="32" type="uint32"/>
        <reg name="r3" bitsize="32" type="uint32"/>
        <reg name="r4" bitsize="32" type="uint32"/>
        <reg name="r5" bitsize="32" type="uint32"/>
        <reg name="r6" bitsize="32" type="uint32"/>
        <reg name="r7" bitsize="32" type="uint32"/>
        <reg name="r8" bitsize="32" type="uint32"/>
        <reg name="r9" bitsize="32" type="uint32"/>
        <reg name="r10" bitsize="32" type="uint32"/>
        <reg name="r11" bitsize="32" type="uint32"/>
        <reg name="r12" bitsize="32" type="uint32"/>
        <reg name="sp" bitsize="32" type="data_ptr"/>
        <reg name="lr" bitsize="32"/>
        <reg name="pc" bitsize="32" type="code_ptr"/>

        <!--
            For some reason, my version of `gdb-multiarch` doesn't seem to
            respect "regnum", and will not parse this custom target.xml unless I
            manually include the padding bytes in the target description.

            On the bright side, AFAIK, there aren't all that many architectures
            that use padding bytes. Heck, the only reason armv4t uses padding is
            for historical reasons (see comment below).

            Odds are if you're defining your own custom arch, you won't run into
            this issue, since you can just lay out all the registers in the
            correct order.
        -->
        <reg name="padding" type="padding" bitsize="32"/>

        <!-- The CPSR is register 25, rather than register 16, because
        the FPA registers historically were placed between the PC
        and the CPSR in the "g" packet. -->
        <reg name="cpsr" bitsize="32" regnum="25"/>
    </feature>
    <xi:include href="extra.xml"/>
</target>#38
 TRACE gdbstub::protocol::recv_packet     > <-- $qXfer:features:read:target.xml:80d,ffb#15
 TRACE gdbstub::protocol::response_writer > --> $l#6c
 TRACE gdbstub::protocol::recv_packet     > <-- $qXfer:features:read:extra.xml:0,ffb#16
 TRACE gdbstub::protocol::response_writer > --> $m<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<feature name="custom-armv4t-extension">
    <!--
        maps to a simple scratch register within the emulator. the GDB
        client can read the register using `p }custom` and set it using
        `set }custom=1337`
    -->
    <reg name="custom" bitsize="32" type="uint32"/>

    <!--
        pseudo-register that return the current time when read.

        notably, i've set up the target to NOT send this register as part of
        the regular register list, which means that GDB will fetch/update
        this register via the 'p' and 'P' packets respectively
    -->
    <reg name="time" bitsize="32" type="uint32"/>

    <!--
        pseudo-register that is always unavailable.

        it is supposed to be reported as 'x'-ed bytes in replies to 'p' packets
        and shown by the GDB client as "<unavailable>".
    -->
    <reg name="unavailable" bitsize="32" type="uint32"/>
</feature>#7d
 TRACE gdbstub::protocol::recv_packet     > <-- $qXfer:features:read:extra.xml:3c5,ffb#b1
 TRACE gdbstub::protocol::response_writer > --> $l#6c
 TRACE gdbstub::protocol::recv_packet     > <-- $qTStatus#49
 TRACE gdbstub::protocol::response_writer > --> $T0;tframes:00#4b
 TRACE gdbstub::protocol::recv_packet     > <-- $qTfV#81
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $?#3f
 TRACE gdbstub::protocol::response_writer > --> $T05thread:p01.01;#06
 TRACE gdbstub::protocol::recv_packet     > <-- $qfThreadInfo#bb
 TRACE gdbstub::protocol::response_writer > --> $mp01.01#cd
 TRACE gdbstub::protocol::recv_packet     > <-- $qsThreadInfo#c8
 TRACE gdbstub::protocol::response_writer > --> $l#6c
 TRACE gdbstub::protocol::recv_packet     > <-- $qAttached:1#fa
GDB queried if it was attached to a process with PID 1
 TRACE gdbstub::protocol::response_writer > --> $1#31
 TRACE gdbstub::protocol::recv_packet     > <-- $qXfer:exec-file:read:1:0,ffb#b7
 TRACE gdbstub::protocol::response_writer > --> $m/test.elf#c1
 TRACE gdbstub::protocol::recv_packet     > <-- $qXfer:exec-file:read:1:9,ffb#c0
 TRACE gdbstub::protocol::response_writer > --> $l#6c
 TRACE gdbstub::protocol::recv_packet     > <-- $Hc-1#09
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- $qOffsets#4b
 TRACE gdbstub::protocol::response_writer > --> $Text=00;Data=00;Bss=00#94
 TRACE gdbstub::protocol::recv_packet     > <-- $g#67
 TRACE gdbstub::protocol::response_writer > --> $00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000107856341200005555xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1000000078563412#0a
 TRACE gdbstub::protocol::recv_packet     > <-- $qfThreadInfo#bb
 TRACE gdbstub::protocol::response_writer > --> $mp01.01#cd
 TRACE gdbstub::protocol::recv_packet     > <-- $qsThreadInfo#c8
 TRACE gdbstub::protocol::response_writer > --> $l#6c
 TRACE gdbstub::protocol::recv_packet     > <-- $qXfer:memory-map:read::0,ffb#18
 TRACE gdbstub::protocol::response_writer > --> $m<?xml version="1.0"?>
<!DOCTYPE memory-map
    PUBLIC "+//IDN gnu.org//DTD GDB Memory Map V1.0//EN"
            "http://sourceware.org/gdb/gdb-memory-map.dtd">
<memory-map>
    <memory type="ram" start="0x20000000" length="0x20000"/>
    <memory type="flash" start="0x08000000" length="0x10000">
        <property name="blocksize">0x4000</property>
    </memory>
    <memory type="flash" start="0x08010000" length="0x10000">
        <property name="blocksize">0x10000</property>
    </memory>
    <memory type="flash" start="0x08020000" length="0x60000">
        <property name="blocksize">0x20000</property>
    </memory>
</memory-map>#da
 TRACE gdbstub::protocol::recv_packet     > <-- $qXfer:memory-map:read::27c,ffb#b4
 TRACE gdbstub::protocol::response_writer > --> $l#6c
 TRACE gdbstub::protocol::recv_packet     > <-- $qSymbol::#5b
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("qSymbol::")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qTStatus#49
 TRACE gdbstub::protocol::response_writer > --> $T0;tframes:00#4b
 TRACE gdbstub::protocol::recv_packet     > <-- $qTfP#7b
 TRACE gdbstub::protocol::response_writer > --> $#00
Before/After `./example_no_std/check_size.sh` output

Before

Finished `release` profile [optimized + debuginfo] target(s) in 0.00s
    Finished `release` profile [optimized + debuginfo] target(s) in 0.00s
    Analyzing target/release/gdbstub-nostd

 File  .text    Size          Crate Name
24.1% 125.7% 17.2KiB   gdbstub_arch <gdbstub_arch::arm::reg::arm_core::ArmCoreRegs as gdbstub::arch::Registers>::gdb_deserialize
24.0% 125.3% 17.2KiB      [Unknown] __mh_execute_header
12.2%  63.7%  8.7KiB      [Unknown] _main
 1.0%   5.4%    764B        gdbstub gdbstub::stub::state_machine::GdbStubStateMachineInner<gdbstub::stub::state_machine::state::Running,T...
 0.5%   2.4%    336B        gdbstub gdbstub::protocol::commands::breakpoint::BasicBreakpoint::from_slice
 0.4%   2.3%    320B        gdbstub <gdbstub::protocol::common::thread_id::ThreadId as core::convert::TryFrom<&[u8]>>::try_from
 0.4%   2.2%    308B        gdbstub gdbstub::protocol::common::hex::decode_hex_buf
 0.4%   2.0%    284B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write
 0.3%   1.7%    236B        gdbstub gdbstub::stub::core_impl::resume::<impl gdbstub::stub::core_impl::GdbStubImpl<T,C>>::write_stop_common
 0.3%   1.7%    232B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_specific_thread_id
 0.3%   1.5%    216B           core core::iter::traits::iterator::Iterator::nth
 0.3%   1.5%    216B           core core::iter::traits::iterator::Iterator::nth
 0.2%   1.1%    160B        gdbstub gdbstub::protocol::common::hex::decode_hex
 0.2%   1.1%    160B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::inner_write
 0.2%   1.1%    156B        gdbstub gdbstub::protocol::common::hex::decode_hex
 0.2%   1.1%    156B        gdbstub gdbstub::protocol::common::hex::decode_hex
 0.2%   1.1%    156B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_num
 0.2%   1.0%    144B        gdbstub <gdbstub::protocol::common::thread_id::IdKind as core::convert::TryFrom<&[u8]>>::try_from
 0.2%   1.0%    144B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_num
 0.2%   1.0%    136B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_hex
 0.2%   0.9%    132B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::flush
 0.2%   0.8%    116B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadBase>::read_a...
 0.1%   0.8%    108B           core <core::slice::iter::SplitMut<T,P> as core::iter::traits::iterator::Iterator>::next
 0.1%   0.8%    108B           core <core::iter::adapters::skip::Skip<I> as core::iter::traits::iterator::Iterator>::next
 0.1%   0.5%     76B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadBase>::write_...
 0.1%   0.5%     76B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadBase>::read_r...
 0.1%   0.5%     76B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadBase>::write_...
 0.1%   0.4%     52B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadResume>::resume
 0.1%   0.4%     52B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadResume>::set_...
 0.1%   0.4%     52B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadResume>::clea...
 0.1%   0.3%     48B  gdbstub_nostd gdbstub_nostd::print_str::print_str
 0.0%   0.1%     12B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::breakpoints::SwBreakpoint>::add_sw_breakpoint
19.2% 100.0% 13.7KiB                .text section size, the file size is 71.6KiB
error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/size: invalid argument -A
Usage: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/size [-m] [-l] [-x] [--] [[-arch <arch_flag>] ...] [file ...]

After

Finished `release` profile [optimized + debuginfo] target(s) in 0.86s
    Finished `release` profile [optimized + debuginfo] target(s) in 0.00s
    Analyzing target/release/gdbstub-nostd

 File  .text    Size          Crate Name
24.1% 125.7% 17.2KiB   gdbstub_arch <gdbstub_arch::arm::reg::arm_core::ArmCoreRegs as gdbstub::arch::Registers>::gdb_deserialize
24.0% 125.3% 17.2KiB      [Unknown] __mh_execute_header
12.2%  63.7%  8.7KiB      [Unknown] _main
 1.0%   5.4%    764B        gdbstub gdbstub::stub::state_machine::GdbStubStateMachineInner<gdbstub::stub::state_machine::state::Running,T...
 0.5%   2.4%    336B        gdbstub gdbstub::protocol::commands::breakpoint::BasicBreakpoint::from_slice
 0.4%   2.3%    320B        gdbstub <gdbstub::protocol::common::thread_id::ThreadId as core::convert::TryFrom<&[u8]>>::try_from
 0.4%   2.2%    308B        gdbstub gdbstub::protocol::common::hex::decode_hex_buf
 0.4%   2.0%    284B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write
 0.3%   1.7%    236B        gdbstub gdbstub::stub::core_impl::resume::<impl gdbstub::stub::core_impl::GdbStubImpl<T,C>>::write_stop_common
 0.3%   1.7%    232B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_specific_thread_id
 0.3%   1.5%    216B           core core::iter::traits::iterator::Iterator::nth
 0.3%   1.5%    216B           core core::iter::traits::iterator::Iterator::nth
 0.2%   1.1%    160B        gdbstub gdbstub::protocol::common::hex::decode_hex
 0.2%   1.1%    160B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::inner_write
 0.2%   1.1%    156B        gdbstub gdbstub::protocol::common::hex::decode_hex
 0.2%   1.1%    156B        gdbstub gdbstub::protocol::common::hex::decode_hex
 0.2%   1.1%    156B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_num
 0.2%   1.0%    144B        gdbstub <gdbstub::protocol::common::thread_id::IdKind as core::convert::TryFrom<&[u8]>>::try_from
 0.2%   1.0%    144B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_num
 0.2%   1.0%    136B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_hex
 0.2%   0.9%    132B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::flush
 0.2%   0.8%    116B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadBase>::read_a...
 0.1%   0.8%    108B           core <core::slice::iter::SplitMut<T,P> as core::iter::traits::iterator::Iterator>::next
 0.1%   0.8%    108B           core <core::iter::adapters::skip::Skip<I> as core::iter::traits::iterator::Iterator>::next
 0.1%   0.5%     76B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadBase>::write_...
 0.1%   0.5%     76B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadBase>::read_r...
 0.1%   0.5%     76B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadBase>::write_...
 0.1%   0.4%     52B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadResume>::resume
 0.1%   0.4%     52B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadResume>::set_...
 0.1%   0.4%     52B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadResume>::clea...
 0.1%   0.3%     48B  gdbstub_nostd gdbstub_nostd::print_str::print_str
 0.0%   0.1%     12B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::breakpoints::SwBreakpoint>::add_sw_breakpoint
19.2% 100.0% 13.7KiB                .text section size, the file size is 71.6KiB
error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/size: invalid argument -A
Usage: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/size [-m] [-l] [-x] [--] [[-arch <arch_flag>] ...] [file ...]

Support reporting errors that conform to core::error::Error. This allows for
calling e.source(), which may provide more information.

As an example, using the `probe-rs` gdbserver. Before:

During the execution of GDB an error was encountered:
Target threw a fatal error: A RISC-V specific error occurred.
 (No further information is available)

After:

During the execution of GDB an error was encountered:
Target threw a fatal error: A RISC-V specific error occurred.
   ... caused by: Error occurred during execution of an abstract command: Exception

This is gated by "std", so should not pose any API changes to deeply
embedded targets.
@xobs xobs force-pushed the add-core-error-support branch from ad2ea07 to 1daea45 Compare November 25, 2025 09:28
@xobs
Copy link
Contributor Author

xobs commented Nov 25, 2025

I'm proposing this change because in probe-rs I keep getting errors along the lines of "An error occurred in probe-rs" with no additional context, because they rely heavily on thiserror which makes liberal use of Error::source(). By supporting this, the chain isn't severed when adding it to gdbstub.

I've managed to work around it by changing the error type that gets propagated, but I think it might still be a nice feature.

@daniel5151
Copy link
Owner

Cross-linking to #112

I agree that having a way to propagate the error source is useful.

That said... relying on Box<dyn core::error::Error> for the implementation seems a bit unfortunate, given the extra indirection / allocation (and, ofc, the reliance on alloc, when core::error::Error doesn't strictly need alloc).

I wonder if an approach like this would work? Just riffing off the top of my head - haven't proven it out in practice.

// add a new default type param to the type (which should be semver compatible?)
pub enum TargetError<E, ECore = NoCoreError> {
    ...
    Fatal(E),
    FatalCore(ECore),
}

// by default, use a ZST to make `FatalCore` be a totally noop variant
enum NoCoreError {}
impl std::core::Error for NoCoreError {}

// tweak the impl bounds here (which _should_ be semver compatible still)
#[cfg(any(feature = "std", feature = "core_error"))]
impl<T, C, ECore> CoreError for GdbStubError<T, C, ECore>
where
    C: Debug + Display,
    T: Debug + Display,
    ECore: CoreError 
{
    fn source(&self) -> Option<&(dyn CoreError + 'static)> {
        let GdbStubError {
            kind: InternalError::FatalCore(e),
        } = self
        else {
            return None;
        };
        e.source()
    }
}

In practice, this is a prime example of a scenario where specialization would really come in clutch, since we'd just be able to specialize the existing TargetError<E> type in instances where E: core::error::Error... but I think this might work as well?

Let me know what you think (or if there's some obvious semver hazard / technical blocker to this idea)

@xobs
Copy link
Contributor Author

xobs commented Nov 29, 2025

Specialization would make so many of these things easier, I agree.

I considered the Box<T> acceptable for now since it only happens once per session, seeing as how it's a fatal error. But your approach seems sound, and is probably a better approach than mine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants