Реализовать что-то похожее на `sd` std :: remove_reference` в Cust? - PullRequest
0 голосов
/ 25 июня 2018

Я хотел бы иметь инструмент для удаления ссылки из типа, если тип является ссылкой.Примерно так (псевдокод):

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 не удовлетворена».Я не уверен, что я просто не понимаю этого или специализация нарушена.(Связано: я получил еще одну странную ошибку с очень похожим кодом здесь )

Я думал о других возможностях для реализации этого: возможно, поместить параметр типа в черту?Может быть, ГАТ могут помочь здесь?Существуют ли в языке другие функции, позволяющие отображать один тип в другой?

Есть ли способ реализовать что-то подобное в Rust?

1 Ответ

0 голосов
/ 26 июня 2018

Вот простой подход, который работает без функции специализации:

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 T и for &'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}`

Сбой по той же причине, что и здесь.: Несоответствие между связанным типом и параметром типа, только когда impl отмечен default Назначение T для WithoutRef в сочетании с default не ограничивает WithoutRef для типа T.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...