proc-macro-workshop:debug-6

审题

// This test case should not require any code change in your macro if you have
// everything up to this point already passing, but is here to demonstrate why
// inferring `#field_ty: Trait` bounds as mentioned in the previous test case is
// not viable.
//
//     #[derive(CustomDebug)]
//     pub struct One<T> {
//         value: T,
//         two: Option<Box<Two<T>>>,
//     }
//
//     #[derive(CustomDebug)]
//     struct Two<T> {
//         one: Box<One<T>>,
//     }
//
// The problematic expansion would come out as:
//
//     impl<T> Debug for One<T>
//     where
//         T: Debug,
//         Option<Box<Two<T>>>: Debug,
//     {...}
//
//     impl<T> Debug for Two<T>
//     where
//         Box<One<T>>: Debug,
//     {...}
//
// There are two things wrong here.
//
// First, taking into account the relevant standard library impls `impl<T> Debug
// for Option<T> where T: Debug` and `impl<T> Debug for Box<T> where T: ?Sized +
// Debug`, we have the following cyclic definition:
//
//   - One<T> implements Debug if there is an impl for Option<Box<Two<T>>>;
//   - Option<Box<Two<T>>> implements Debug if there is an impl for Box<Two<T>>;
//   - Box<Two<T>> implements Debug if there is an impl for Two<T>;
//   - Two<T> implements Debug if there is an impl for Box<One<T>>;
//   - Box<One<T>> implements Debug if there is an impl for One<T>; cycle!
//
// The Rust compiler detects and rejects this cycle by refusing to assume that
// an impl for any of these types exists out of nowhere. The error manifests as:
//
//     error[E0275]: overflow evaluating the requirement `One<u8>: std::fmt::Debug`
//      -->
//       |     assert_debug::<One<u8>>();
//       |     ^^^^^^^^^^^^^^^^^^^^^^^
//
// There is a technique known as co-inductive reasoning that may allow a
// revamped trait solver in the compiler to process cycles like this in the
// future, though there is still uncertainty about whether co-inductive
// semantics would lead to unsoundness in some situations when applied to Rust
// trait impls. There is no current activity pursuing this but some discussion
// exists in a GitHub issue called "#[derive] sometimes uses incorrect bounds":
// https://github.com/rust-lang/rust/issues/26925
//
// The second thing wrong is a private-in-public violation:
//
//     error[E0446]: private type `Two<T>` in public interface
//      -->
//       |   struct Two<T> {
//       |   - `Two<T>` declared as private
//     ...
//       | / impl<T> Debug for One<T>
//       | | where
//       | |     T: Debug,
//       | |     Option<Box<Two<T>>>: Debug,
//     ... |
//       | | }
//       | |_^ can't leak private type
//
// Public APIs in Rust are not allowed to be defined in terms of private types.
// That includes the argument types and return types of public function
// signatures, as well as trait bounds on impls of public traits for public
// types.

use derive_debug::CustomDebug;
use std::fmt::Debug;

#[derive(CustomDebug)]
pub struct One<T> {
    value: T,
    two: Option<Box<Two<T>>>,
}

#[derive(CustomDebug)]
struct Two<T> {
    one: Box<One<T>>,
}

fn assert_debug<F: Debug>() {}

fn main() {
    assert_debug::<One<u8>>();
    assert_debug::<Two<u8>>();
}

这道题主要向我们展示了为何第五题最好做泛型部分限定的原因。

如题所示,其中One中有TwoTwo中有One
如果真的要全部类型限定,必然涉及到路径上的全部包装类型。

比如针对A<B<C<D<E>>>>,相关的类型就会有ABCDE五个,逐层的展开,不仅麻烦,如果在这道题中,可能还会无限的嵌套。
而实际上,对于A<B<C<D<E>>>>,我们实际上只用关心A即可。

题解

debug-5已经解答了,这里只是深化一下。

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!