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

Luk*_*odt 11 rust

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

remove_ref(i32)      == i32
remove_ref(&i32)     == i32
remove_ref(&&i32)    == i32
remove_ref(&mut i32) == i32
Run Code Online (Sandbox Code Playgroud)

C++ std::remove_reference在标准库中有完全符合我的要求.我尝试在Rust中实现相同的功能,但我无法使其正常工作.现在,在Rust中"输出"类型的唯一方法是关于特征的关联类型(我认为).我试过这样的事情(游乐场):

#![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;
}
Run Code Online (Sandbox Code Playgroud)

事实上,这编译.有希望!(是的,这还没有考虑到可变引用).但是,当我尝试使用它时,一切都会爆炸:

let _: <i32 as RemoveRef>::WithoutRef = 3;
let _: <&i32 as RemoveRef>::WithoutRef = 3;
let _: <&&i32 as RemoveRef>::WithoutRef = 3;
Run Code Online (Sandbox Code Playgroud)

第一行导致"溢出评估要求i32: RemoveRef".另外两行产生错误"特征限制&i32: RemoveRef不满足".我不确定我是否只是不理解这一点,或者是否打破了专业化.(相关:我在这里有非常相似的代码的另一个奇怪的错误)

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

有没有办法在Rust中实现这样的东西?

Cal*_*tor 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;
}
Run Code Online (Sandbox Code Playgroud)

不确定它是否可以与您在此表单中的实际用例兼容,或者它是否有用.

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

impl RemoveRef for i32 {
    type WithoutRef = Self;
}
Run Code Online (Sandbox Code Playgroud)

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

let _: <i32 as RemoveRef>::WithoutRef = 3;
let _: <&i32 as RemoveRef>::WithoutRef = 3;
let _: <&&i32 as RemoveRef>::WithoutRef = 3;
Run Code Online (Sandbox Code Playgroud)

AFAIK特不能帮你解决问题,重叠等之间的一个for T,并for &'a T在这一点上.它需要像负特征边界这样的特征.

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

impl<T> RemoveRef for T {
    default type WithoutRef = T;
}
Run Code Online (Sandbox Code Playgroud)
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}`
Run Code Online (Sandbox Code Playgroud)

此失败的相同的理由,这里讨论:仅当实现了一套标记相关联的类型和类型参数之间不匹配default分配TWithoutRef结合default并不限制WithoutRef输入T.