可以对引用和非引用做泛型吗?
Can one do generics over references and non-references?
我正在尝试编写代码,根据需要使引用成为非 Copy
类型,同时如果它是 Copy
(因为它是引用)则直接使用该值。考虑以下示例:
struct Wrapper<F>(F);
impl<F> Wrapper<F> {
fn f<'a, G, O>(&'a self, other: &Wrapper<G>) -> O
where for<'r> &'r G: Add<&'a F, Output = O> {
&other.0 + &self.0
}
}
以上代码编译正常。我现在想用几种不同的类型调用该方法(或类似方法)。有些是参考,有些不是:
// Three different ways of calling
fn g1<'a, T, S>(
x: &'a Wrapper<T>, y: &'a Wrapper<T>,
) -> S
where for<'r> &'r T: Add<&'a T> {
x.f(&y)
}
fn g2<T, S>(
x: &Wrapper<T>, y: &Wrapper<&T>,
) -> S
where for<'r> &'r T: Add<&'r T> {
x.f(&y)
}
fn g3<T, S>(
x: &Wrapper<&T>, y: &Wrapper<&T>,
) -> S
where for<'r> &'r T: Add<&'r T> {
x.f(&y)
}
这行不通,因为 Wrapper::f
的接口似乎限制太多。
有没有一种方法可以编写 Wrapper::f
以便可以将其与引用类型和非引用类型一起使用,而不需要对 F <-> &&G
组合使用 impl?
当你听到 "generic over references and non-references" 时,想想 std::borrow::Borrow<T>
。 Borrow<T>
由多种指针实现,如Arc<T>
、Box<T>
和&T
,以及普通的T
.
impl<F> Wrapper<F> {
fn f<'a, T, G>(&'a self, other: &'a Wrapper<G>) -> <&'a T as Add>::Output
where
F: Borrow<T>,
G: Borrow<T>,
&'a T: Add<&'a T>,
{
other.0.borrow() + self.0.borrow()
}
}
(请注意,HRTB(for<'r>
边界)通常仅在引用 inside 具有边界的函数时才需要。在 f
,引用由调用者传入,因此 &'a T
上的正则绑定就足够了,并允许 f
更通用。)
上述方法适用于 g1
、g2
和 g3
(once you add Output = S
),因为只有一个 T
可以同时使用两种类型Borrow
到。也许令人惊讶的是,它也适用于具体的 F
或 G
,它们只为一种类型 T
实现 Borrow<T>
,例如 i32
(仅实现 Borrow<i32>
).但是,如果您尝试使用同时实现 Borrow
的类型多次调用 f
,编译器将无法推断出 T
。这是一个使用 &i32
的示例,它实现了 Borrow<&i32>
和 Borrow<i32>
:
fn gx<'a>(x: &'a Wrapper<&i32>, y: &'a Wrapper<&i32>) -> i32 {
x.f(&y) // error[E0284]: type annotations needed: cannot satisfy `<&_ as std::ops::Add>::Output == i32`
}
发生这种情况时,您必须使用 turbofish:
明确指定 T
fn gx<'a>(x: &'a Wrapper<&i32>, y: &'a Wrapper<&i32>) -> i32 {
x.f::<i32, _>(&y) // ok
}
我正在尝试编写代码,根据需要使引用成为非 Copy
类型,同时如果它是 Copy
(因为它是引用)则直接使用该值。考虑以下示例:
struct Wrapper<F>(F);
impl<F> Wrapper<F> {
fn f<'a, G, O>(&'a self, other: &Wrapper<G>) -> O
where for<'r> &'r G: Add<&'a F, Output = O> {
&other.0 + &self.0
}
}
以上代码编译正常。我现在想用几种不同的类型调用该方法(或类似方法)。有些是参考,有些不是:
// Three different ways of calling
fn g1<'a, T, S>(
x: &'a Wrapper<T>, y: &'a Wrapper<T>,
) -> S
where for<'r> &'r T: Add<&'a T> {
x.f(&y)
}
fn g2<T, S>(
x: &Wrapper<T>, y: &Wrapper<&T>,
) -> S
where for<'r> &'r T: Add<&'r T> {
x.f(&y)
}
fn g3<T, S>(
x: &Wrapper<&T>, y: &Wrapper<&T>,
) -> S
where for<'r> &'r T: Add<&'r T> {
x.f(&y)
}
这行不通,因为 Wrapper::f
的接口似乎限制太多。
有没有一种方法可以编写 Wrapper::f
以便可以将其与引用类型和非引用类型一起使用,而不需要对 F <-> &&G
组合使用 impl?
当你听到 "generic over references and non-references" 时,想想 std::borrow::Borrow<T>
。 Borrow<T>
由多种指针实现,如Arc<T>
、Box<T>
和&T
,以及普通的T
.
impl<F> Wrapper<F> {
fn f<'a, T, G>(&'a self, other: &'a Wrapper<G>) -> <&'a T as Add>::Output
where
F: Borrow<T>,
G: Borrow<T>,
&'a T: Add<&'a T>,
{
other.0.borrow() + self.0.borrow()
}
}
(请注意,HRTB(for<'r>
边界)通常仅在引用 inside 具有边界的函数时才需要。在 f
,引用由调用者传入,因此 &'a T
上的正则绑定就足够了,并允许 f
更通用。)
上述方法适用于 g1
、g2
和 g3
(once you add Output = S
),因为只有一个 T
可以同时使用两种类型Borrow
到。也许令人惊讶的是,它也适用于具体的 F
或 G
,它们只为一种类型 T
实现 Borrow<T>
,例如 i32
(仅实现 Borrow<i32>
).但是,如果您尝试使用同时实现 Borrow
的类型多次调用 f
,编译器将无法推断出 T
。这是一个使用 &i32
的示例,它实现了 Borrow<&i32>
和 Borrow<i32>
:
fn gx<'a>(x: &'a Wrapper<&i32>, y: &'a Wrapper<&i32>) -> i32 {
x.f(&y) // error[E0284]: type annotations needed: cannot satisfy `<&_ as std::ops::Add>::Output == i32`
}
发生这种情况时,您必须使用 turbofish:
明确指定T
fn gx<'a>(x: &'a Wrapper<&i32>, y: &'a Wrapper<&i32>) -> i32 {
x.f::<i32, _>(&y) // ok
}