2015-05-17 3 views
1

Пусть следующие объекты:Принудительно формула проверить признак

pub struct MyStruct<T>{ 
    items: Vec<T> 
} 

pub impl<T> MyStruct<T> { 
    pub fn new() -> MyStruct { 
    MyStruct{ items::new(); } 
    } 
    pub fn add(&mut self, item: T) where T : Eq { 
    self.items.push(item); 
    } 
} 

pub trait C {} 

pub struct Another { 
    mystruct: MyStruct<Box<C>> 
} 

pub impl Another { 
    pub fn exec<C>(&mut self, c: C) where C: Eq + C { 
    self.mystruct.add(c); 
    } 
} 

На Exec я в жизнь, что C также является уравнение, но я получаю следующее сообщение об ошибке:

error: the trait `core::cmp::Eq` is not implemented for the type `C` 

я имел чтобы

pub impl<T> MyStruct<T> 

вместо

pub impl<T : Eq> MyStruct<T> 

потому что, поскольку C - это признак, я не могу применять Eq при использовании MyStruct :: new, поэтому я оставил проверку для защиты типа функции. Что тут происходит?

+0

Ваш примерный код не воспроизводит ошибку, о которой вы заявляете (это не [MCVE] (http://stackoverflow.com/help/mcve)). Если это случится, вы можете получить больше ответов. Кроме того, стиль Rust - это 4-х сторонние отступы. – Shepmaster

+1

Поскольку у Криса Моргана есть ответ, я также укажу, что ваша реализация 'add' имеет параметр типа' T', который затеняет параметр типа struct, а в вашей реализации 'exec' параметр типа 'C' затеняет черту' C', что приводит к потенциально запутанному сообщению об ошибке. – Shepmaster

+0

@Shepmaster, спасибо за отзыв. Фактически, у add не было шаблона, это было вызвано ошибкой копирования/вставки и переименованием класса (компиляция этого приводит к ошибке при затенении, как вы упомянули). В следующий раз я попытаюсь создать MCVE, чтобы лучше воспроизвести проблему. – scooterman

ответ

3

Давайте посмотрим на соответствующие определения для Eq:

pub trait Eq: PartialEq<Self> { 
    … 
} 

pub trait PartialEq<Rhs: ?Sized = Self> { 
    fn eq(&self, other: &Rhs) -> bool; 
    … 
} 

Теперь рассмотрим MyStruct<Box<C>>: тип, который он хочет реализовать Eq является Box<C>, коробочная объект черта. Для реализации Eq, Box<C> необходимо сначала осуществить PartialEq<Box<C>>, как это:

impl PartialEq for Box<C> { 
    fn eq(&self, other: &Box<C>) -> bool; 
} 

impl Eq for Box<C> { } 

То есть, вы должны быть в состоянии сравнить произвольный Box<C> с любым другим произвольным Box<C>. Здесь объекты с боксами, которые вы сравниваете, могут иметь разные конкретные типы. Таким образом, вам нужно написать эту реализацию вручную. В таких случаях вы, как правило, хотите, чтобы эта черта включала некоторый способ нормализации формы объекта в конкретный сопоставимый тип; для некоторых типов это очевидно; если AsRef<T> должен был иметь добавил PartialEq реализация, реализация добавить бы довольно очевидно:

impl<T: PartialEq> PartialEq for AsRef<T> { 
    fn eq(&self, other: &AsRef<T>) -> bool { 
     self.as_ref() == other.as_ref() 
    } 
} 

(Заметим также, что если C реализует PartialEq, Box<C> делает, поэтому такие реализации, как мы обсуждали, должны идти на unboxed trait object, а не на объект с боксами.)

Довольно часто, однако, есть не очевидная и простая реализация. Есть несколько подходов можно принять:

  1. Преобразование объектов (потенциально дорого, хотя в идеале дешево) в какой-то базовый тип, например, a String, который затем можно сравнить;

  2. Отказ;

  3. Constrain C к 'static типы и использовать некоторые фантазии Any магию, чтобы сделать так, что он использует PartialEq реализацию базового типа, возвращая false, если два объекта Trait не одного и того же конкретного типа.Это одна довольно полезная, так что я дам код для него:

    #![feature(core)] 
    
    use std::any::{Any, TypeId}; 
    use std::mem; 
    
    fn main() { } 
    
    trait PartialEqFromC { 
        fn eq_c(&self, other: &C) -> bool; 
    } 
    
    impl<T: PartialEq + Any + C> PartialEqFromC for T { 
        fn eq_c(&self, other: &C) -> bool { 
         if other.get_type_id() == TypeId::of::<Self>() { 
          self == unsafe { *mem::transmute::<&&C, &&Self>(&other) } 
         } else { 
          false 
         } 
        } 
    } 
    
    trait C: Any + PartialEqFromC { 
    } 
    
    impl PartialEq for C { 
        fn eq(&self, other: &C) -> bool { 
         self.eq_c(other) 
        } 
    } 
    

    Обратите внимание, что этот пример зависит от нестабильной функции core для Any.get_type_id и, таким образом, привязанную к ночам только; это можно обойти, дублируя это определение с черты Any в новый супертрайт C, а также может быть упрощено на mopafying the C trait.

+0

Жаль, что я не мог присудить дополнительное голосование за вариант 2.^_^ – Shepmaster

+0

фантастический ответ! спасибо за это подробное объяснение и за ссылку для вашей библиотеки, которая дает пищу для размышлений. Один вопрос, такой вид использования - идиоматическая ржавчина? Моя основная идея заключалась в том, чтобы создать коллекцию Bag, где T будет идентифицироваться (через Eq). – scooterman

+0

Использование объектов-признаков не является особенно распространенным, и выставлять «Любые» или подобные вещи обычно не являются идиоматическими. Его можно, однако, использовать очень законно внутри страны. AnyMap, например, использует 'Any' внутренне, но (если вы не погружаетесь в необработанный интерфейс, который также теперь отображается для некоторых неясных ситуаций) предоставляет интерфейс, полностью лишенный объектов объектов« Любой ». –

Смежные вопросы