custom error implementation

#![feature(backtrace)]
type Error = Box<dyn std::error::Error+Send+Sync>;
use std::backtrace::{BacktraceStatus,Backtrace};

#[derive(Debug)]
pub struct ParseError {
  kind: ParseErrorKind,
  backtrace: Backtrace,
}

#[derive(Debug)]
pub enum ParseErrorKind {
  InsufficientBytes { required: usize, received: usize }
}

impl std::error::Error for ParseError {
  fn backtrace<'a>(&'a self) -> Option<&'a Backtrace> {
    Some(&self.backtrace)
  }
}

impl std::fmt::Display for ParseError {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    match &self.kind {
      ParseErrorKind::InsufficientBytes { required, received } => {
        write!(f, "required {} bytes while parsing but received {}", required, received)
      }
    }
  }
}

fn parse(src: &[u8]) -> Result<u32,ParseError> {
  if src.len() < 4 {
    Err(ParseError {
      backtrace: Backtrace::capture(),
      kind: ParseErrorKind::InsufficientBytes { required: 4, received: src.len() }
    })
  } else {
    Ok(u32::from_be_bytes([src[0],src[1],src[2],src[3]]))
  }
}

fn main() -> Result<(),Error> {
  let buf = vec![1,2,3,4,5,6,7,8];
  println!["# desired main() error formatting (via Display + backtrace):\n"];
  if let Err(e) = parse(&buf[0..3]) {
    match std::error::Error::backtrace(&e) {
      Some(bt) => match bt.status() {
        BacktraceStatus::Captured => eprint!["{}\n{}", e, bt],
        _ => eprint!["{}", e],
      },
      None => eprint!["{}", e],
    }
  }
  println!["\n"];
  println!["# default main() error formatting (via Debug):\n"];
  parse(&buf[0..3])?;
  Ok(())
}

output without backtraces:

$ cargo run -q
# desired main() error formatting (via Display + backtrace):

required 4 bytes while parsing but received 3

# default main() error formatting (via Debug):

Error: ParseError { kind: InsufficientBytes { required: 4, received: 3 }, backtrace: <disabled> }

output with backtraces:

$ RUST_BACKTRACE=full cargo run -q
# desired main() error formatting (via Display + backtrace):

required 4 bytes while parsing but received 3
   0: err::parse
             at ./main.rs:35:18
   1: err::main
             at ./main.rs:46:19
   2: core::ops::function::FnOnce::call_once
             at /rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/core/src/ops/function.rs:227:5
   3: std::sys_common::backtrace::__rust_begin_short_backtrace
             at /rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/std/src/sys_common/backtrace.rs:125:18
   4: std::rt::lang_start::{{closure}}
             at /rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/std/src/rt.rs:66:18
   5: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once
             at /rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/core/src/ops/function.rs:259:13
      std::panicking::try::do_call
             at /rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/std/src/panicking.rs:379:40
      std::panicking::try
             at /rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/std/src/panicking.rs:343:19
      std::panic::catch_unwind
             at /rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/std/src/panic.rs:431:14
      std::rt::lang_start_internal
             at /rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/std/src/rt.rs:51:25
   6: std::rt::lang_start
             at /rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/std/src/rt.rs:65:5
   7: main
   8: __libc_start_main
   9: _start


# default main() error formatting (via Debug):

Error: ParseError { kind: InsufficientBytes { required: 4, received: 3 }, backtrace: Backtrace [{ fn: "err::parse", file: "./main.rs", line: 35 }, { fn: "err::main", file: "./main.rs", line: 57 }, { fn: "core::ops::function::FnOnce::call_once", file: "/rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/core/src/ops/function.rs", line: 227 }, { fn: "std::sys_common::backtrace::__rust_begin_short_backtrace", file: "/rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/std/src/sys_common/backtrace.rs", line: 125 }, { fn: "std::rt::lang_start::{{closure}}", file: "/rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/std/src/rt.rs", line: 66 }, { fn: "core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once", file: "/rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/core/src/ops/function.rs", line: 259 }, { fn: "std::panicking::try::do_call", file: "/rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/std/src/panicking.rs", line: 379 }, { fn: "std::panicking::try", file: "/rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/std/src/panicking.rs", line: 343 }, { fn: "std::panic::catch_unwind", file: "/rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/std/src/panic.rs", line: 431 }, { fn: "std::rt::lang_start_internal", file: "/rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/std/src/rt.rs", line: 51 }, { fn: "std::rt::lang_start", file: "/rustc/9d9c2c92b834c430f102ea96f65119e37320776e/library/std/src/rt.rs", line: 65 }, { fn: "main" }, { fn: "__libc_start_main" }, { fn: "_start" }] }