在Rust中实现类似于C ++的`std :: remove_reference`吗?

时间:2018-06-25 15:02:25

标签: rust

如果类型是引用,我想拥有一个从类型中删除引用的工具。这样的东西(伪代码):

remove_ref(i32)      == i32
remove_ref(&i32)     == i32
remove_ref(&&i32)    == i32
remove_ref(&mut i32) == i32

C ++在标准库中有std::remove_reference,可以完全满足我的要求。我试图在Rust中实现相同的功能,但无法正常工作。目前,在Rust中“输出”类型的唯一方法是与特征相关联的类型(我认为)。我尝试过这样的事情(Playground):

#![feature(specialization)]

trait RemoveRef {
    type WithoutRef;
}

default impl<T> RemoveRef for T {
    type WithoutRef = T;
}

impl<'a, T: RemoveRef> RemoveRef for &'a T {
    type WithoutRef = T::WithoutRef;
}

实际上,它可以编译。有希望! (是的,这还不能说明可变的引用)。但是,当我尝试使用它时,一切都会爆炸:

let _: <i32 as RemoveRef>::WithoutRef = 3;
let _: <&i32 as RemoveRef>::WithoutRef = 3;
let _: <&&i32 as RemoveRef>::WithoutRef = 3;

第一行导致“溢出评估需求i32: RemoveRef”。其他两行产生错误“不满足特征绑定&i32: RemoveRef”。我不确定我是否不理解这一点,或者专业化是否中断。 (相关:使用非常相似的代码here我又遇到了另一个奇怪的错误)

我在考虑实现此目的的其他可能性:也许在特征上放一个类型参数?也许GAT可以在这里提供帮助?语言中是否还有其他功能允许从一种类型映射到另一种类型?

有什么方法可以在Rust中实现这样的功能?

1 个答案:

答案 0 :(得分:5)

这是一种简单的方法,不需要专门的功能:

use std::marker::PhantomData;

trait RemoveRef {
    type WithoutRef;
}

struct Ref<T> {
    phantom: PhantomData<T>,
}

impl<T> RemoveRef for Ref<T> {
    type WithoutRef = T;
}

impl<'a, T: RemoveRef> RemoveRef for &'a T {
    type WithoutRef = T::WithoutRef;
}

fn main() {
    let _: <Ref<i32> as RemoveRef>::WithoutRef = 3;
    let _: <&Ref<i32> as RemoveRef>::WithoutRef = 3;
    let _: <&&&&Ref<i32> as RemoveRef>::WithoutRef = 3;
}

不确定是否可以这种形式使其与您的实际用例兼容,或者它是否有用。

或者,当然也可以将具体的退出条件(impl<T> RemoveRef for T)替换为具体类型的实现:

impl RemoveRef for i32 {
    type WithoutRef = Self;
}

这将启用您的原始测试代码:

let _: <i32 as RemoveRef>::WithoutRef = 3;
let _: <&i32 as RemoveRef>::WithoutRef = 3;
let _: <&&i32 as RemoveRef>::WithoutRef = 3;

AFAIK专业化目前无法帮助您解决for Tfor &'a T之间的重叠问题。这将需要特征负边界之类的特征。

default impl中的所有项目都是隐式默认的。如果将default关键字移到代码中的关联类型,则可以避免评估溢出,但是会出现其他错误:

impl<T> RemoveRef for T {
    default type WithoutRef = T;
}
error[E0308]: mismatched types
  --> src/main.rs:16:45
   |
16 |     let _: <i32 as RemoveRef>::WithoutRef = 3;
   |                                             ^ expected associated type, found integral variable
   |
   = note: expected type `<i32 as RemoveRef>::WithoutRef`
              found type `{integer}`

此操作失败的原因与此处讨论的原因相同:Mismatch between associated type and type parameter only when impl is marked defaultTWithoutRef一起分配给default并不约束WithoutRef键入{{ 1}}。

相关问题