首页 文章

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

提问于
浏览
10

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

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

C在标准库中有std::remove_reference,可以完全按照我的意愿行事 . 我尝试在Rust中实现相同的功能,但我无法使其正常工作 . 现在,Rust中"output"类型的唯一方法是特征上的关联类型(我认为) . 我试过这样的事情(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 ". The other two lines produce the error "特征限制 &i32: RemoveRef 不满意” . 我是理解这一点,或者是否打破了专业化 . (相关:我有一个非常相似的代码here的另一个奇怪的错误)

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

Is there any way to implement something like this in Rust?

1 回答

  • 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 组合 WithoutRef 并不会将 WithoutRef 约束为 T .

相关问题