我正在尝试创建一个包含实现特征的结构,该特征可以是自有类型或引用计数指针,例如
Arc
、Rc
等。到目前为止,我已经使用 实现了以下内容然而,Borrow
特性,编译器强制我注释 new
函数返回的实例的类型,这对于我的板条箱用户来说并不理想。
如何修改下面的代码,让用户不需要注释
new
函数返回的实例的类型?
struct Foo<B, T> {
borrow: B,
_marker: std::marker::PhantomData<T>
}
impl<B, T> Foo<B, T> {
fn new(borrow: B) -> Self {
Self {
borrow,
_marker: Default::default()
}
}
}
impl <B, T> Foo<B, T> where
B: std::borrow::Borrow<T>,
T: std::fmt::Display {
fn print(&self) {
println!("{}", self.borrow.borrow())
}
}
fn main() {
// owned string (remove type annotation for error)
let f: Foo<_, String> =
Foo::new(String::from("hello"));
f.print();
// string slice (remove type annotation for error)
let f: Foo<_, &str> =
Foo::new("hello");
f.print();
// string in Arc (remove type annotation for error)
let f: Foo<_, std::sync::Arc<String>> =
Foo::new(std::sync::Arc::new(String::from("hello")));
f.print();
}
您的代码的问题在于,您的
B
可以为许多不同的 Borrow<T>
类型实现 T
,因此,如果您不以某种方式指定 T
,则调用将不明确。 AsRef
也是如此。
但这不是为了
Deref
。每个类型只能实现一次,并且已经为任何 std
智能指针实现了。
代码很简单,因为您可以约束
Deref::Target
类型:
use std::ops::Deref;
impl<B> Foo<B>
where
B: Deref,
<B as Deref>::Target: std::fmt::Display,
{
fn print(&self) {
println!("{}", self.obj.deref());
}
}
这有一个缺点,只适用于类似智能指针的类型。也就是说,如果
B = i32
它将不起作用,因为诸如 i32
之类的普通类型无法实现 Deref
:
let f: Foo<_> = Foo::new(42);
f.print();
> error[E0599]: the method `print` exists for struct `Foo<{integer}>`, but its > trait bounds were not satisfied
> note: trait bound `{integer}: Deref` was not satisfied
> where
> B: Deref,
> ^^^^^ unsatisfied trait bound introduced here
如果您觉得可以,您可以在这里停止阅读。
如果这不行,我认为最好的选择是忘记
AsRef/Deref/Borrow
的事情,只要求 B
实现你想要的特征,或者该特征的外观。
具体细节会根据您想要实现的具体内容而有所不同。如果你的特质实际上是
Display
并且不是占位符,那么你实际上不需要任何特殊的东西,因为 Display
是为 Box<T>
、Rc<T>
、Arc<T>
等实现的,当 T: Display
时。所以你只需写:
impl<B> Foo<B>
where B: std::fmt::Display,
{
fn print(&self) {
println!("{}", self.borrow)
}
}
但我猜你的实际特质不是
Display
。同样,细节会根据您是否拥有该特征而有所不同。想象一下这个特征:
trait Printable {
fn print(&self);
}
impl Printable for str {
fn print(&self) { println!("s:{}", self); }
}
impl Printable for i32 {
fn print(&self) { println!("i:{}", self); }
}
如果是你的,你可以直接编写以下代码,但如果是第三方的,你将需要一个 facade 特征:
trait AsPrintable {
type Target: Printable + ?Sized;
fn as_printable(&self) -> &Self::Target;
}
你的
Foo
将是:
impl<B> Foo<B>
where
B: AsPrintable,
{
fn print(&self) {
self.obj.as_printable().print();
}
}
然后您可以为您想要的任何类型实现该特征。这里的小不便之处在于您需要两个编写两个毯子实现:
impl<T: Printable> AsPrintable for T {
type Target = T;
fn as_printable(&self) -> &T { self }
}
impl<T: Deref> AsPrintable for T
where <T as Deref>::Target: AsPrintable
{
type Target = <<T as Deref>::Target as AsPrintable>::Target;
fn as_printable(&self) -> &Self::Target { self.deref().as_display() }
}
但是只允许其中之一,Rustc 由于不明确而不会同时允许两者。如果需要,可以通过对所需包装器进行一些手动实现来修复此问题。或者用新类型来替换第一个毯子实现,例如这个:
struct MakePrintable<T>(T);
impl<T: Printable> AsPrintable for MakePrintable<T> {
type Target = T;
fn as_printable(&self) -> &T { &self.0 }
}
这是一个包含整个示例的游乐场。