可以对 return 引用或拥有的值实施的特征方法
Trait method that can be implemented to either return a reference or an owned value
我正在尝试使用一种方法定义特征,该方法可以实现为 return 引用或拥有的值。
类似于:
struct Type;
trait Trait {
type Value;
fn f(&self) -> Self::Value;
}
impl Trait for () {
type Value = Type;
fn f(&self) -> Self::Value {
Type
}
}
impl Trait for (Type,) {
type Value = &Type; // error[E0106]: missing lifetime specifier
fn f(&self) -> Self::Value {
&self.0
}
}
虽然这段代码不起作用,因为 &Type
缺少生命周期说明符。我希望 &Type
与 &self
具有相同的生命周期(即 fn f<'a>(&'a self) -> &'a Type
),但我不知道如何在 Rust 中表达这一点。
我设法找到了几种方法来使这段代码工作,但我都不喜欢其中任何一种:
为特征本身添加明确的生命周期:
trait Trait<'a> {
type Value;
fn f<'b>(&'b self) -> Self::Value where 'b: 'a;
}
impl<'a> Trait<'a> for () {
type Value = Type;
fn f<'b>(&'b self) -> Self::Value
where 'b: 'a
{
Type
}
}
impl<'a> Trait<'a> for (Type,) {
type Value = &'a Type;
fn f<'b>(&'b self) -> Self::Value
where 'b: 'a
{
&self.0
}
}
我不喜欢这个解决方案的地方是任何使用 Trait
的东西都需要一个明确的生命周期(我认为这不是本质上必需的),而且这个特性似乎实现起来不必要地复杂。
返回可能是也可能不是参考的内容 - 例如 std::borrow::Cow
:
trait Trait {
type Value;
fn f<'a>(&'a self) -> Cow<'a, Self::Value>;
}
impl Trait for () {
type Value = Type;
fn f<'a>(&'a self) -> Cow<'a, Self::Value> {
Cow::Owned(Type)
}
}
impl Trait for (Type,) {
type Value = Type;
fn f<'a>(&'a self) -> Cow<'a, Self::Value> {
Cow::Borrowed(&self.0)
}
}
我不喜欢这个解决方案的地方是 ().f()
是一个 Cow<_>
:我需要调用 ().f().into_owned()
来获取我的 Type
。这似乎是不必要的(并且在使用 Trait
作为特征对象时可能会导致一些可忽略的 运行 时间开销)。
还要注意 Cow
不好,因为它要求 Self::Value
实现 ToOwned
(thus, practically, Clone
),这个要求太强了。无论如何,在没有这种限制的情况下,很容易实现 Cow
的替代方案。
这个问题还有其他解决办法吗? standard/most common/preferred 是什么?
这可以使用额外的关联对象来解决,以在 return 类型或引用之间进行选择,再加上一些元编程魔法。
首先,一些助手类型:
struct Value;
struct Reference;
trait ReturnKind<'a, T: ?Sized + 'a> {
type Type: ?Sized;
}
impl<'a, T: ?Sized + 'a> ReturnKind<'a, T> for Value {
type Type = T;
}
impl<'a, T: ?Sized + 'a> ReturnKind<'a, T> for Reference {
type Type = &'a T;
}
ReturnKind
是一个 "type-level function",当 "input" 是 Value
时,return 是 T
,而 &T
是Reference
.
然后是特征:
trait Trait {
type Value;
type Return: for<'a> ReturnKind<'a, Self::Value>;
fn f<'a>(&'a self) -> <Self::Return as ReturnKind<'a, Self::Value>>::Type;
}
我们通过 "calling" 类型级函数 ReturnKind
生成 return 类型。
"input argument" Return
需要实现 trait 才能让我们写 <Return as ReturnKind<'a, Value>>
。尽管我们不知道 Self 的生命周期究竟是多少,但我们可以使用 HRTB Return: for<'a> ReturnKind<'a, Value>
使 Return
受到 所有 可能生命周期的约束。 =32=]
用法:
impl Trait for () {
type Value = f64;
type Return = Value;
fn f(&self) -> f64 {
42.0
}
}
impl Trait for (f64,) {
type Value = f64;
type Return = Reference;
fn f(&self) -> &f64 {
&self.0
}
}
fn main() {
let a: (f64,) = ( ().f(), );
let b: &f64 = a.f();
println!("{:?} {:?}", a, b);
// (42,) 42
}
请注意,以上仅在 Value
类型具有 'static
生命周期时有效。如果 Value
本身有一个有限的生命周期,这个生命周期必须被 Trait
知道。由于 Rust doesn't support associated lifetimes yet,它必须像 Trait<'foo>
一样使用,不幸的是:
struct Value;
struct Reference;
struct ExternalReference;
trait ReturnKind<'a, 's, T: ?Sized + 'a + 's> {
type Type: ?Sized;
}
impl<'a, 's, T: ?Sized + 'a + 's> ReturnKind<'a, 's, T> for Value {
type Type = T;
}
impl<'a, 's, T: ?Sized + 'a + 's> ReturnKind<'a, 's, T> for Reference {
type Type = &'a T;
}
impl<'a, 's, T: ?Sized + 'a + 's> ReturnKind<'a, 's, T> for ExternalReference {
type Type = &'s T;
}
trait Trait<'s> {
type Value: 's;
type Return: for<'a> ReturnKind<'a, 's, Self::Value>;
fn f<'a>(&'a self) -> <Self::Return as ReturnKind<'a, 's, Self::Value>>::Type;
}
impl Trait<'static> for () {
type Value = f64;
type Return = Value;
fn f(&self) -> f64 {
42.0
}
}
impl Trait<'static> for (f64,) {
type Value = f64;
type Return = Reference;
fn f(&self) -> &f64 {
&self.0
}
}
impl<'a> Trait<'a> for (&'a f64,) {
type Value = f64;
type Return = ExternalReference;
fn f(&self) -> &'a f64 {
self.0
}
}
fn main() {
let a: (f64,) = ( ().f(), );
let b: &f64 = a.f();
let c: &f64 = (b,).f();
println!("{:?} {:?} {:?}", a, b, c);
// (42,) 42 42
}
但是如果在 trait 上设置生命周期参数没问题,那么 OP 已经提供了一个更简单的解决方案:
trait Trait<'a> {
type Value;
fn f<'b>(&'b self) -> Self::Value where 'b: 'a;
}
impl<'a> Trait<'a> for () {
type Value = f64;
fn f<'b: 'a>(&'b self) -> Self::Value {
42.0
}
}
impl<'a> Trait<'a> for (f64,) {
type Value = &'a f64;
fn f<'b: 'a>(&'b self) -> Self::Value {
&self.0
}
}
impl<'a, 's> Trait<'s> for (&'a f64,) {
type Value = &'a f64;
fn f<'b: 's>(&'b self) -> Self::Value {
self.0
}
}
fn main() {
let a: (f64,) = ( ().f(), );
let b: &f64 = a.f();
let c: &f64 = (b,).f();
println!("{:?} {:?} {:?}", a, b, c);
// (42,) 42 42
}
@kennytm 提出了一个很好的(如果复杂的话)解决方案;我想提出一个更简单的替代方案。
有两种可能为值提供生命周期名称:
- 在特质层面:
trait Trait<'a> { ... }
- 在方法层面:
trait Trait { fn f<'a>(&'a self) -> ... }
后者的语言支持不是很好,虽然更灵活但也更复杂。但是,也有前者通常就足够的情况;因此,不用多说,我向您介绍:
trait Trait<'a> {
type Value;
fn f(self) -> Self::Value;
}
f
消耗它的输出,如果 Self
是一个不可变的引用就没问题,因为那些是 Copy
.
证据就在布丁里:
struct Type;
impl Trait<'static> for () {
type Value = Type;
fn f(self) -> Self::Value {
Type
}
}
impl<'a> Trait<'a> for &'a (Type,) {
type Value = &'a Type;
fn f(self) -> Self::Value {
&self.0
}
}
并且可以毫无问题地调用它:
fn main(){
().f();
(Type,).f();
}
这个解决方案肯定没有那么灵活;但它也简单得多。
我正在尝试使用一种方法定义特征,该方法可以实现为 return 引用或拥有的值。
类似于:
struct Type;
trait Trait {
type Value;
fn f(&self) -> Self::Value;
}
impl Trait for () {
type Value = Type;
fn f(&self) -> Self::Value {
Type
}
}
impl Trait for (Type,) {
type Value = &Type; // error[E0106]: missing lifetime specifier
fn f(&self) -> Self::Value {
&self.0
}
}
虽然这段代码不起作用,因为 &Type
缺少生命周期说明符。我希望 &Type
与 &self
具有相同的生命周期(即 fn f<'a>(&'a self) -> &'a Type
),但我不知道如何在 Rust 中表达这一点。
我设法找到了几种方法来使这段代码工作,但我都不喜欢其中任何一种:
为特征本身添加明确的生命周期:
trait Trait<'a> { type Value; fn f<'b>(&'b self) -> Self::Value where 'b: 'a; } impl<'a> Trait<'a> for () { type Value = Type; fn f<'b>(&'b self) -> Self::Value where 'b: 'a { Type } } impl<'a> Trait<'a> for (Type,) { type Value = &'a Type; fn f<'b>(&'b self) -> Self::Value where 'b: 'a { &self.0 } }
我不喜欢这个解决方案的地方是任何使用
Trait
的东西都需要一个明确的生命周期(我认为这不是本质上必需的),而且这个特性似乎实现起来不必要地复杂。返回可能是也可能不是参考的内容 - 例如
std::borrow::Cow
:trait Trait { type Value; fn f<'a>(&'a self) -> Cow<'a, Self::Value>; } impl Trait for () { type Value = Type; fn f<'a>(&'a self) -> Cow<'a, Self::Value> { Cow::Owned(Type) } } impl Trait for (Type,) { type Value = Type; fn f<'a>(&'a self) -> Cow<'a, Self::Value> { Cow::Borrowed(&self.0) } }
我不喜欢这个解决方案的地方是
().f()
是一个Cow<_>
:我需要调用().f().into_owned()
来获取我的Type
。这似乎是不必要的(并且在使用Trait
作为特征对象时可能会导致一些可忽略的 运行 时间开销)。还要注意
Cow
不好,因为它要求Self::Value
实现ToOwned
(thus, practically,Clone
),这个要求太强了。无论如何,在没有这种限制的情况下,很容易实现Cow
的替代方案。
这个问题还有其他解决办法吗? standard/most common/preferred 是什么?
这可以使用额外的关联对象来解决,以在 return 类型或引用之间进行选择,再加上一些元编程魔法。
首先,一些助手类型:
struct Value;
struct Reference;
trait ReturnKind<'a, T: ?Sized + 'a> {
type Type: ?Sized;
}
impl<'a, T: ?Sized + 'a> ReturnKind<'a, T> for Value {
type Type = T;
}
impl<'a, T: ?Sized + 'a> ReturnKind<'a, T> for Reference {
type Type = &'a T;
}
ReturnKind
是一个 "type-level function",当 "input" 是 Value
时,return 是 T
,而 &T
是Reference
.
然后是特征:
trait Trait {
type Value;
type Return: for<'a> ReturnKind<'a, Self::Value>;
fn f<'a>(&'a self) -> <Self::Return as ReturnKind<'a, Self::Value>>::Type;
}
我们通过 "calling" 类型级函数 ReturnKind
生成 return 类型。
"input argument" Return
需要实现 trait 才能让我们写 <Return as ReturnKind<'a, Value>>
。尽管我们不知道 Self 的生命周期究竟是多少,但我们可以使用 HRTB Return: for<'a> ReturnKind<'a, Value>
使 Return
受到 所有 可能生命周期的约束。 =32=]
用法:
impl Trait for () {
type Value = f64;
type Return = Value;
fn f(&self) -> f64 {
42.0
}
}
impl Trait for (f64,) {
type Value = f64;
type Return = Reference;
fn f(&self) -> &f64 {
&self.0
}
}
fn main() {
let a: (f64,) = ( ().f(), );
let b: &f64 = a.f();
println!("{:?} {:?}", a, b);
// (42,) 42
}
请注意,以上仅在 Value
类型具有 'static
生命周期时有效。如果 Value
本身有一个有限的生命周期,这个生命周期必须被 Trait
知道。由于 Rust doesn't support associated lifetimes yet,它必须像 Trait<'foo>
一样使用,不幸的是:
struct Value;
struct Reference;
struct ExternalReference;
trait ReturnKind<'a, 's, T: ?Sized + 'a + 's> {
type Type: ?Sized;
}
impl<'a, 's, T: ?Sized + 'a + 's> ReturnKind<'a, 's, T> for Value {
type Type = T;
}
impl<'a, 's, T: ?Sized + 'a + 's> ReturnKind<'a, 's, T> for Reference {
type Type = &'a T;
}
impl<'a, 's, T: ?Sized + 'a + 's> ReturnKind<'a, 's, T> for ExternalReference {
type Type = &'s T;
}
trait Trait<'s> {
type Value: 's;
type Return: for<'a> ReturnKind<'a, 's, Self::Value>;
fn f<'a>(&'a self) -> <Self::Return as ReturnKind<'a, 's, Self::Value>>::Type;
}
impl Trait<'static> for () {
type Value = f64;
type Return = Value;
fn f(&self) -> f64 {
42.0
}
}
impl Trait<'static> for (f64,) {
type Value = f64;
type Return = Reference;
fn f(&self) -> &f64 {
&self.0
}
}
impl<'a> Trait<'a> for (&'a f64,) {
type Value = f64;
type Return = ExternalReference;
fn f(&self) -> &'a f64 {
self.0
}
}
fn main() {
let a: (f64,) = ( ().f(), );
let b: &f64 = a.f();
let c: &f64 = (b,).f();
println!("{:?} {:?} {:?}", a, b, c);
// (42,) 42 42
}
但是如果在 trait 上设置生命周期参数没问题,那么 OP 已经提供了一个更简单的解决方案:
trait Trait<'a> {
type Value;
fn f<'b>(&'b self) -> Self::Value where 'b: 'a;
}
impl<'a> Trait<'a> for () {
type Value = f64;
fn f<'b: 'a>(&'b self) -> Self::Value {
42.0
}
}
impl<'a> Trait<'a> for (f64,) {
type Value = &'a f64;
fn f<'b: 'a>(&'b self) -> Self::Value {
&self.0
}
}
impl<'a, 's> Trait<'s> for (&'a f64,) {
type Value = &'a f64;
fn f<'b: 's>(&'b self) -> Self::Value {
self.0
}
}
fn main() {
let a: (f64,) = ( ().f(), );
let b: &f64 = a.f();
let c: &f64 = (b,).f();
println!("{:?} {:?} {:?}", a, b, c);
// (42,) 42 42
}
@kennytm 提出了一个很好的(如果复杂的话)解决方案;我想提出一个更简单的替代方案。
有两种可能为值提供生命周期名称:
- 在特质层面:
trait Trait<'a> { ... }
- 在方法层面:
trait Trait { fn f<'a>(&'a self) -> ... }
后者的语言支持不是很好,虽然更灵活但也更复杂。但是,也有前者通常就足够的情况;因此,不用多说,我向您介绍:
trait Trait<'a> {
type Value;
fn f(self) -> Self::Value;
}
f
消耗它的输出,如果 Self
是一个不可变的引用就没问题,因为那些是 Copy
.
证据就在布丁里:
struct Type;
impl Trait<'static> for () {
type Value = Type;
fn f(self) -> Self::Value {
Type
}
}
impl<'a> Trait<'a> for &'a (Type,) {
type Value = &'a Type;
fn f(self) -> Self::Value {
&self.0
}
}
并且可以毫无问题地调用它:
fn main(){
().f();
(Type,).f();
}
这个解决方案肯定没有那么灵活;但它也简单得多。