1use alloc::boxed::Box;
2use alloc::rc::Rc;
3use alloc::sync::Arc;
4use core::error;
5use core::panic::Location;
6
7#[diagnostic::on_unimplemented(
15 message = "`{Self}` cannot build an error from a source of type `{E}`",
16 label = "this selector does not accept a `{E}` source",
17 note = "a selector whose variant has a `source` field builds from that field's exact type — \
18 `.context(...)` on a `Result` requires the selector's source type to match the \
19 `Result`'s error",
20 note = "a selector with no `source` field is a leaf: build it with `.build()` / `.fail()`, or \
21 attach it to an `Option` with `.context(...)` — not to a `Result`'s error"
22)]
23pub trait Contextual<E> {
24 type Destination;
26
27 #[track_caller]
29 fn build_error(self, source: E) -> Self::Destination;
30}
31
32pub trait Capturable {
46 #[track_caller]
48 fn capture() -> Self;
49
50 #[track_caller]
57 #[inline]
58 fn capture_or_extract(source: &dyn crate::Diagnostic) -> Self
59 where
60 Self: Sized,
61 {
62 let _ = source;
63 Self::capture()
64 }
65}
66
67impl<T: Capturable> Capturable for Box<T> {
68 #[track_caller]
69 #[inline]
70 fn capture() -> Self {
71 Self::new(T::capture())
72 }
73
74 #[track_caller]
75 #[inline]
76 fn capture_or_extract(source: &dyn crate::Diagnostic) -> Self {
77 Self::new(T::capture_or_extract(source))
78 }
79}
80
81impl<T: Capturable> Capturable for Rc<T> {
82 #[track_caller]
83 #[inline]
84 fn capture() -> Self {
85 Self::new(T::capture())
86 }
87
88 #[track_caller]
89 #[inline]
90 fn capture_or_extract(source: &dyn crate::Diagnostic) -> Self {
91 Self::new(T::capture_or_extract(source))
92 }
93}
94
95impl<T: Capturable> Capturable for Arc<T> {
96 #[track_caller]
97 #[inline]
98 fn capture() -> Self {
99 Self::new(T::capture())
100 }
101
102 #[track_caller]
103 #[inline]
104 fn capture_or_extract(source: &dyn crate::Diagnostic) -> Self {
105 Self::new(T::capture_or_extract(source))
106 }
107}
108
109macro_rules! impl_capturable_tuples {
110 ($($name:ident),* $(,)?) => {
112 impl_capturable_tuples!(@acc [] $($name,)*);
113 };
114 (@acc [$($acc:ident,)*] $head:ident, $($rest:ident,)*) => {
116 impl_capturable_tuples!(@impl $($acc,)* $head,);
117 impl_capturable_tuples!(@acc [$($acc,)* $head,] $($rest,)*);
118 };
119 (@acc [$($acc:ident,)*]) => {};
121 (@impl $($name:ident,)+) => {
122 impl<$($name: Capturable),+> Capturable for ($($name,)+) {
123 #[track_caller]
124 #[inline]
125 fn capture() -> Self {
126 ($($name::capture(),)+)
127 }
128 #[track_caller]
129 #[inline]
130 fn capture_or_extract(source: &dyn crate::Diagnostic) -> Self {
131 ($($name::capture_or_extract(source),)+)
132 }
133 }
134 };
135}
136impl_capturable_tuples!(A, B, C, D, E, F, G, H, I);
137
138#[cfg(feature = "std")]
139impl Capturable for std::time::SystemTime {
140 #[inline]
141 fn capture() -> Self {
142 Self::now()
143 }
144}
145
146#[cfg(feature = "std")]
147impl Capturable for std::time::Instant {
148 #[inline]
149 fn capture() -> Self {
150 Self::now()
151 }
152}
153impl Capturable for &'static Location<'static> {
154 #[inline]
155 #[track_caller]
156 fn capture() -> Self {
157 Location::caller()
158 }
159
160 #[track_caller]
161 #[inline]
162 fn capture_or_extract(source: &dyn crate::Diagnostic) -> Self {
163 let here = Location::caller();
167 source.oopsie_location().unwrap_or(here)
168 }
169}
170
171#[cfg(feature = "chrono")]
172impl Capturable for chrono::DateTime<chrono::Local> {
173 #[inline]
174 fn capture() -> Self {
175 chrono::Local::now()
176 }
177}
178
179#[cfg(feature = "jiff")]
180impl Capturable for jiff::Timestamp {
181 #[inline]
182 fn capture() -> Self {
183 Self::now()
184 }
185}
186
187#[cfg(feature = "jiff")]
188impl Capturable for jiff::Zoned {
189 #[inline]
190 fn capture() -> Self {
191 Self::now()
192 }
193}
194
195#[derive(Debug, Copy, Clone, PartialEq, Eq)]
200pub struct NoSource;
201
202pub trait AsErrorSource {
204 fn as_error_source(&self) -> &(dyn error::Error + 'static);
206}
207
208impl<T: error::Error + 'static> AsErrorSource for T {
209 #[inline]
210 fn as_error_source(&self) -> &(dyn error::Error + 'static) {
211 self
212 }
213}
214
215impl AsErrorSource for dyn error::Error + 'static {
216 #[inline]
217 fn as_error_source(&self) -> &(dyn error::Error + 'static) {
218 self
219 }
220}
221
222impl AsErrorSource for dyn error::Error + Send + 'static {
223 #[inline]
224 fn as_error_source(&self) -> &(dyn error::Error + 'static) {
225 self
226 }
227}
228
229impl AsErrorSource for dyn error::Error + Send + Sync + 'static {
230 #[inline]
231 fn as_error_source(&self) -> &(dyn error::Error + 'static) {
232 self
233 }
234}
235
236pub trait ResultExt<T, E> {
259 fn context<C>(self, context: C) -> Result<T, C::Destination>
261 where
262 C: Contextual<E>;
263
264 fn with_context<F, C>(self, context: F) -> Result<T, C::Destination>
266 where
267 F: FnOnce(&E) -> C,
268 C: Contextual<E>;
269
270 fn boxed(self) -> Result<T, Box<dyn error::Error + Send + Sync + 'static>>
275 where
276 E: error::Error + Send + Sync + 'static;
277
278 fn boxed_local(self) -> Result<T, Box<dyn error::Error + 'static>>
283 where
284 E: error::Error + 'static;
285}
286
287impl<T, E> ResultExt<T, E> for Result<T, E> {
288 #[inline]
289 #[track_caller]
290 fn context<C>(self, context: C) -> Result<T, C::Destination>
291 where
292 C: Contextual<E>,
293 {
294 match self {
298 Ok(value) => Ok(value),
299 Err(error) => Err(context.build_error(error)),
300 }
301 }
302
303 #[inline]
304 #[track_caller]
305 fn with_context<F, C>(self, context: F) -> Result<T, C::Destination>
306 where
307 F: FnOnce(&E) -> C,
308 C: Contextual<E>,
309 {
310 match self {
311 Ok(value) => Ok(value),
312 Err(error) => {
313 let selector = context(&error);
314 Err(selector.build_error(error))
315 }
316 }
317 }
318
319 #[inline]
320 fn boxed(self) -> Result<T, Box<dyn error::Error + Send + Sync + 'static>>
321 where
322 E: error::Error + Send + Sync + 'static,
323 {
324 self.map_err(|error| Box::new(error) as Box<dyn error::Error + Send + Sync + 'static>)
325 }
326
327 #[inline]
328 fn boxed_local(self) -> Result<T, Box<dyn error::Error + 'static>>
329 where
330 E: error::Error + 'static,
331 {
332 self.map_err(|error| Box::new(error) as Box<dyn error::Error + 'static>)
333 }
334}
335
336pub trait OptionExt<T> {
359 fn context<C>(self, context: C) -> Result<T, C::Destination>
361 where
362 C: Contextual<NoSource>;
363
364 fn with_context<F, C>(self, context: F) -> Result<T, C::Destination>
366 where
367 F: FnOnce() -> C,
368 C: Contextual<NoSource>;
369}
370
371impl<T> OptionExt<T> for Option<T> {
372 #[inline]
373 #[track_caller]
374 fn context<C>(self, context: C) -> Result<T, C::Destination>
375 where
376 C: Contextual<NoSource>,
377 {
378 match self {
379 Some(value) => Ok(value),
380 None => Err(context.build_error(NoSource)),
381 }
382 }
383
384 #[inline]
385 #[track_caller]
386 fn with_context<F, C>(self, context: F) -> Result<T, C::Destination>
387 where
388 F: FnOnce() -> C,
389 C: Contextual<NoSource>,
390 {
391 match self {
392 Some(value) => Ok(value),
393 None => Err(context().build_error(NoSource)),
394 }
395 }
396}
397
398#[cfg(test)]
399mod tests {
400 use super::*;
401 use std::error::Error as StdError;
402 use std::fmt;
403 use std::time::SystemTime;
404
405 #[cfg(feature = "jiff")]
406 #[test]
407 fn captures_current_jiff_timestamp() {
408 let before = jiff::Timestamp::now();
411 let captured = <jiff::Timestamp as Capturable>::capture();
412 assert!(captured >= before, "captured {captured} predates {before}");
413 }
414
415 #[cfg(feature = "jiff")]
416 #[test]
417 fn captures_current_jiff_zoned() {
418 let before = jiff::Timestamp::now();
419 let captured = <jiff::Zoned as Capturable>::capture().timestamp();
420 assert!(captured >= before, "captured {captured} predates {before}");
421 }
422
423 #[derive(Debug)]
424 struct DiagOnly;
425
426 impl fmt::Display for DiagOnly {
427 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
428 f.write_str("diag only")
429 }
430 }
431
432 impl std::error::Error for DiagOnly {}
433 impl crate::Diagnostic for DiagOnly {}
434
435 #[derive(Debug)]
437 struct SimpleError {
438 message: String,
439 }
440
441 impl fmt::Display for SimpleError {
442 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443 write!(f, "{}", self.message)
444 }
445 }
446
447 impl std::error::Error for SimpleError {}
448
449 #[derive(Debug)]
450 struct SourceError {
451 message: String,
452 }
453
454 impl fmt::Display for SourceError {
455 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456 write!(f, "{}", self.message)
457 }
458 }
459
460 impl std::error::Error for SourceError {}
461
462 #[derive(Debug)]
463 struct ChainError {
464 message: String,
465 source: Box<SourceError>,
466 }
467
468 impl fmt::Display for ChainError {
469 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470 write!(f, "{}", self.message)
471 }
472 }
473
474 impl std::error::Error for ChainError {
475 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
476 Some(&*self.source)
477 }
478 }
479
480 struct SimpleSelector;
482
483 impl Contextual<NoSource> for SimpleSelector {
484 type Destination = SimpleError;
485
486 fn build_error(self, _source: NoSource) -> SimpleError {
487 SimpleError {
488 message: "simple error".to_owned(),
489 }
490 }
491 }
492
493 struct ChainSelector;
494
495 impl Contextual<SourceError> for ChainSelector {
496 type Destination = ChainError;
497
498 fn build_error(self, source: SourceError) -> ChainError {
499 ChainError {
500 message: "chain error".to_owned(),
501 source: Box::new(source),
502 }
503 }
504 }
505
506 #[test]
507 fn test_result_ext_context_ok() {
508 let result: Result<i32, SourceError> = Ok(42);
509 let chained: Result<i32, ChainError> = result.context(ChainSelector);
510 assert!(chained.is_ok());
511 assert_eq!(chained.unwrap(), 42);
512 }
513
514 #[test]
515 fn test_result_ext_context_err() {
516 let result: Result<i32, SourceError> = Err(SourceError {
517 message: "source failed".to_owned(),
518 });
519 let chained: Result<i32, ChainError> = result.context(ChainSelector);
520 assert!(chained.is_err());
521 let err = chained.unwrap_err();
522 assert_eq!(err.message, "chain error");
523 assert_eq!(StdError::source(&err).unwrap().to_string(), "source failed");
524 }
525
526 #[test]
527 fn test_result_ext_with_context_ok() {
528 let result: Result<i32, SourceError> = Ok(42);
529 let chained: Result<i32, ChainError> = result.with_context(|_| ChainSelector);
530 assert!(chained.is_ok());
531 assert_eq!(chained.unwrap(), 42);
532 }
533
534 #[test]
535 fn test_result_ext_with_context_err_closure_called() {
536 let mut closure_called = false;
537 let result: Result<i32, SourceError> = Err(SourceError {
538 message: "source failed".to_owned(),
539 });
540 let chained: Result<i32, ChainError> = result.with_context(|source| {
541 closure_called = true;
542 assert_eq!(source.message, "source failed");
544 ChainSelector
545 });
546 assert!(closure_called);
547 chained.unwrap_err();
548 }
549
550 #[test]
551 fn test_option_ext_context_some() {
552 let option: Option<i32> = Some(42);
553 let result: Result<i32, SimpleError> = option.context(SimpleSelector);
554 assert!(result.is_ok());
555 assert_eq!(result.unwrap(), 42);
556 }
557
558 #[test]
559 fn test_option_ext_context_none() {
560 let option: Option<i32> = None;
561 let result: Result<i32, SimpleError> = option.context(SimpleSelector);
562 assert!(result.is_err());
563 assert_eq!(result.unwrap_err().message, "simple error");
564 }
565
566 #[test]
567 fn test_option_ext_with_context_some() {
568 let option: Option<i32> = Some(42);
569 let result: Result<i32, SimpleError> = option.with_context(|| SimpleSelector);
570 assert!(result.is_ok());
571 assert_eq!(result.unwrap(), 42);
572 }
573
574 #[test]
575 fn test_option_ext_with_context_none_closure_called() {
576 let mut closure_called = false;
577 let option: Option<i32> = None;
578 let result: Result<i32, SimpleError> = option.with_context(|| {
579 closure_called = true;
580 SimpleSelector
581 });
582 assert!(closure_called);
583 result.unwrap_err();
584 }
585
586 const _: () = {
587 const fn is_capturable<T: Capturable>() {}
589 is_capturable::<Box<crate::Backtrace>>();
590 };
591
592 #[cfg(feature = "tracing")]
593 #[test]
594 fn tuple_capture_produces_both_elements() {
595 use crate::{Backtrace, SpanTrace};
596 let (bt, st): (Backtrace, SpanTrace) = <(Backtrace, SpanTrace) as Capturable>::capture();
598 let _ = bt.frames();
599 let _ = st.status();
600 }
601
602 #[cfg(feature = "tracing")]
603 const _: () = {
604 const fn is_capturable<T: Capturable>() {}
605 is_capturable::<Box<(crate::Backtrace, crate::SpanTrace)>>();
606 is_capturable::<(crate::Backtrace, crate::SpanTrace)>();
607 };
608
609 #[cfg(feature = "tracing")]
610 #[test]
611 fn tuple_capture_ext_extracts_both_from_source() {
612 use crate::{
613 Backtrace, Capturable, Diagnostic, RustBacktrace, SpanTrace,
614 with_rust_backtrace_override,
615 };
616 use std::fmt;
617
618 #[derive(Debug)]
619 struct Src {
620 backtrace: Backtrace,
621 spantrace: SpanTrace,
622 }
623 impl fmt::Display for Src {
624 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
625 f.write_str("src")
626 }
627 }
628 impl std::error::Error for Src {}
629 impl Diagnostic for Src {
630 fn oopsie_backtrace(&self) -> Option<&Backtrace> {
631 Some(&self.backtrace)
632 }
633 fn oopsie_spantrace(&self) -> Option<&SpanTrace> {
634 Some(&self.spantrace)
635 }
636 }
637
638 with_rust_backtrace_override(RustBacktrace::Enabled, || {
639 let src = Src {
640 backtrace: Backtrace::capture(),
641 spantrace: SpanTrace::capture(),
642 };
643 let source_frames = src.backtrace.frames().len();
644 assert!(
645 source_frames > 0,
646 "backtrace must be enabled for this test to be probative"
647 );
648 let extracted = <(Backtrace, SpanTrace) as Capturable>::capture_or_extract(&src);
649 assert_eq!(extracted.0.frames().len(), source_frames);
652 });
653 }
654
655 #[cfg(feature = "tracing")]
656 const _: () = {
657 const fn is_capturable<T: Capturable>() {}
658 is_capturable::<Box<(crate::Backtrace, crate::SpanTrace)>>();
659 is_capturable::<(crate::Backtrace, crate::SpanTrace)>();
660 };
661
662 #[derive(Debug)]
666 struct BoxedChainError {
667 message: String,
668 source: Box<dyn StdError + Send + Sync + 'static>,
669 }
670
671 impl fmt::Display for BoxedChainError {
672 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
673 write!(f, "{}", self.message)
674 }
675 }
676
677 impl StdError for BoxedChainError {
678 fn source(&self) -> Option<&(dyn StdError + 'static)> {
679 Some(&*self.source)
680 }
681 }
682
683 struct BoxedSelector;
684
685 impl Contextual<Box<dyn StdError + Send + Sync + 'static>> for BoxedSelector {
686 type Destination = BoxedChainError;
687
688 fn build_error(self, source: Box<dyn StdError + Send + Sync + 'static>) -> BoxedChainError {
689 BoxedChainError {
690 message: "boxed chain error".to_owned(),
691 source,
692 }
693 }
694 }
695
696 #[derive(Debug)]
697 struct BoxedLocalChainError {
698 source: Box<dyn StdError + 'static>,
699 }
700
701 impl fmt::Display for BoxedLocalChainError {
702 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
703 write!(f, "boxed local chain error")
704 }
705 }
706
707 impl StdError for BoxedLocalChainError {
708 fn source(&self) -> Option<&(dyn StdError + 'static)> {
709 Some(&*self.source)
710 }
711 }
712
713 struct BoxedLocalSelector;
714
715 impl Contextual<Box<dyn StdError + 'static>> for BoxedLocalSelector {
716 type Destination = BoxedLocalChainError;
717
718 fn build_error(self, source: Box<dyn StdError + 'static>) -> BoxedLocalChainError {
719 BoxedLocalChainError { source }
720 }
721 }
722
723 #[test]
724 fn boxed_passes_through_ok() {
725 let result: Result<i32, SourceError> = Ok(7);
726 assert_eq!(result.boxed().unwrap(), 7);
727 }
728
729 #[test]
730 fn boxed_erases_err_but_preserves_display_and_concrete_type() {
731 let result: Result<i32, SourceError> = Err(SourceError {
732 message: "boom".to_owned(),
733 });
734 let err = result.boxed().unwrap_err();
735 assert_eq!(err.to_string(), "boom");
736 assert!(err.downcast_ref::<SourceError>().is_some());
738 }
739
740 #[test]
741 fn boxed_local_erases_err_but_preserves_display_and_concrete_type() {
742 let result: Result<i32, SourceError> = Err(SourceError {
743 message: "boom".to_owned(),
744 });
745 let err = result.boxed_local().unwrap_err();
746 assert_eq!(err.to_string(), "boom");
747 assert!(err.downcast_ref::<SourceError>().is_some());
748 }
749
750 #[test]
751 fn boxed_then_context_chains_erased_source() {
752 let result: Result<i32, SourceError> = Err(SourceError {
753 message: "io broke".to_owned(),
754 });
755 let chained: Result<i32, BoxedChainError> = result.boxed().context(BoxedSelector);
756 let err = chained.unwrap_err();
757 assert_eq!(err.message, "boxed chain error");
758 assert_eq!(StdError::source(&err).unwrap().to_string(), "io broke");
759 }
760
761 #[test]
762 fn boxed_then_context_passes_through_ok() {
763 let result: Result<i32, SourceError> = Ok(42);
764 let chained: Result<i32, BoxedChainError> = result.boxed().context(BoxedSelector);
765 assert_eq!(chained.unwrap(), 42);
766 }
767
768 #[test]
769 fn boxed_then_with_context_chains_erased_source() {
770 let result: Result<i32, SourceError> = Err(SourceError {
771 message: "io broke".to_owned(),
772 });
773 let chained: Result<i32, BoxedChainError> = result.boxed().with_context(|source| {
774 assert_eq!(source.to_string(), "io broke");
776 BoxedSelector
777 });
778 assert_eq!(
779 StdError::source(&chained.unwrap_err()).unwrap().to_string(),
780 "io broke"
781 );
782 }
783
784 #[test]
785 fn boxed_local_then_context_chains_erased_source() {
786 let result: Result<i32, SourceError> = Err(SourceError {
787 message: "io broke".to_owned(),
788 });
789 let chained: Result<i32, BoxedLocalChainError> =
790 result.boxed_local().context(BoxedLocalSelector);
791 assert_eq!(
792 StdError::source(&chained.unwrap_err()).unwrap().to_string(),
793 "io broke"
794 );
795 }
796
797 #[test]
798 fn boxed_unifies_heterogeneous_error_types() {
799 fn run(fail_with_source: bool) -> Result<(), Box<dyn StdError + Send + Sync + 'static>> {
802 if fail_with_source {
803 Err(SourceError {
804 message: "source".to_owned(),
805 })
806 .boxed()?;
807 } else {
808 Err(SimpleError {
809 message: "simple".to_owned(),
810 })
811 .boxed()?;
812 }
813 Ok(())
814 }
815 assert_eq!(run(true).unwrap_err().to_string(), "source");
816 assert_eq!(run(false).unwrap_err().to_string(), "simple");
817 }
818
819 #[test]
820 fn boxed_on_boxed_error_downcasts_to_the_box() {
821 let result: Result<(), Box<SourceError>> = Err(Box::new(SourceError {
822 message: "boom".to_owned(),
823 }));
824 let err = result.boxed().unwrap_err();
825 assert!(err.downcast_ref::<SourceError>().is_none());
826 assert!(err.downcast_ref::<Box<SourceError>>().is_some());
827 }
828
829 const _: () = {
830 const fn is_send_sync<T: Send + Sync>() {}
832 is_send_sync::<Box<dyn StdError + Send + Sync + 'static>>();
833 };
834
835 #[derive(Debug)]
836 struct WithLoc(&'static std::panic::Location<'static>);
837
838 impl fmt::Display for WithLoc {
839 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
840 f.write_str("with loc")
841 }
842 }
843
844 impl StdError for WithLoc {}
845
846 impl crate::Diagnostic for WithLoc {
847 fn oopsie_location(&self) -> Option<&'static std::panic::Location<'static>> {
848 Some(self.0)
849 }
850 }
851
852 #[test]
853 fn location_captures_caller_and_extracts_from_source() {
854 let line = line!() + 1;
856 let loc = <&'static std::panic::Location<'static> as Capturable>::capture();
857 assert!(loc.file().ends_with("traits.rs"));
858 assert_eq!(loc.line(), line);
859
860 let src = WithLoc(std::panic::Location::caller());
862 let extracted =
863 <&'static std::panic::Location<'static> as Capturable>::capture_or_extract(&src);
864 assert!(
865 std::ptr::eq(extracted, src.0),
866 "must reuse the source's location"
867 );
868
869 let fresh =
871 <&'static std::panic::Location<'static> as Capturable>::capture_or_extract(&DiagOnly);
872 assert!(fresh.file().ends_with("traits.rs"));
873 }
874
875 #[test]
876 fn system_time_captures_now_and_never_extracts() {
877 let before = SystemTime::now();
878 let captured = <SystemTime as Capturable>::capture();
879 assert!(captured >= before);
880 let extracted = <SystemTime as Capturable>::capture_or_extract(&DiagOnly);
881 assert!(extracted >= before);
882 }
883
884 #[test]
885 fn location_captures() {
886 let location = <&'static Location<'static> as Capturable>::capture();
887 let expected_line = line!() - 1; assert_eq!(location.line(), expected_line);
889 assert_eq!(location.file(), file!());
890 }
891
892 #[test]
893 fn location_captures_nested() {
894 #[track_caller]
895 fn capture_location() -> &'static Location<'static> {
896 <&'static Location<'static> as Capturable>::capture()
897 }
898 let location = capture_location();
899 let expected_line = line!() - 1; assert_eq!(location.line(), expected_line);
901 assert_eq!(location.file(), file!());
902 }
903
904 #[test]
905 fn complex_location_captures() {
906 type Loc = &'static Location<'static>;
907 type LocTimeLoc = (Loc, SystemTime, Loc);
908 let location =
909 <(Box<LocTimeLoc>, Rc<LocTimeLoc>, Arc<LocTimeLoc>) as Capturable>::capture();
910 let expected_line = line!() - 1; let expected_col = 3 * 4 + 1; let (box_loc, rc_loc, arc_loc) = location;
913 for (loc1, _, loc2) in [&*box_loc, &*rc_loc, &*arc_loc] {
914 for loc in [loc1, loc2] {
915 assert_eq!(loc.file(), file!());
916 assert_eq!(loc.line(), expected_line);
917 assert_eq!(loc.column(), expected_col);
918 }
919 }
920 }
921}