Невозможно вызвать функцию со ссылкой на тип, реализующий признак

Мне сложно понять, как работать с чертами и владениями. Следующий пример работает:

struct X([u8; 4]);

impl X {
   pub fn get(&self, n: usize) -> u8 {
       self.0[n]
   }
}

fn f1(x: &X) {
    println!("{}", x.get(1));
    f2(&x);
}

fn f2(x: &X) {
    println!("{}", x.get(2));    
}

fn main() {
    let z1 = X([1u8, 2u8, 3u8, 4u8]);
    f1(&z1);
}

Но когда я пытаюсь создать трейт (здесь XT) с помощью get:

trait XT {
  fn get(&self, n: usize) -> u8;
}

struct X([u8; 4]);

impl XT for X {
   fn get(&self, n: usize) -> u8 {
       self.0[n]
   }
}

fn f1<T: XT>(x: &T) {
    println!("{}", x.get(1));
    f2(&x);
}

fn f2<T: XT>(x: &T) {
    println!("{}", x.get(2));    
}

fn main() {
    let z1 = X([1u8, 2u8, 3u8, 4u8]);
    f1(&z1);
}

Не удается скомпилировать следующее сообщение об ошибке:

признак XT не реализован для типа &T

Работает, если поменять f2(&x) на f2(x). Я ожидал, что заменив типы чертами, все будет работать.


person Hernan    schedule 27.09.2015    source источник


Ответы (1)


Проблема в том, что вы пытаетесь передать &&T f2. Это означает, что он ожидает, что &T реализует XT, а это не то, что вы сказали: вы сказали, что T реализует XT.

Вы можете изменить f1, чтобы правильно выразить это ограничение, используя предложение where T: XT, for<'a> &'a T: XT, но тогда вы не сможете вызвать f1 из main, потому что &X не реализует XT. Итак, вы идете и добавляете реализацию для этого, и тогда код работает ... но, честно говоря, проще просто удалить это & и вместо этого вызвать f2(x).

Другими словами: просто потому, что тип реализует черту, не означает, что указатели на этот тип также реализуют черту.

person DK.    schedule 27.09.2015
comment
Но смущает то, что в первом случае он работает нормально. (Отказ от ответственности: у меня нет полного представления о Rust, поэтому я могу ошибаться) Это выглядит как очень распространенный сценарий: вы пишете некоторую функцию, которая принимает определенный тип, а затем понимаете, что можете использовать их для большего количества вещей. Поэтому вы создаете признак и заменяете (с некоторым синтаксисом) тип признака в определениях функций. Синтаксис, который заставляет его работать, не очень эргономичен. - person Hernan; 27.09.2015
comment
@Hernan Это работает в первом случае, потому что z1 имеет тип X, поэтому &z1 имеет тип &X. Когда вы вызываете f1, компилятор делает вывод, что &T = &X, следовательно, T = X. Когда вы вызываете f2 с помощью &x, он получает &T = &&X, поэтому T = &X, а &X не реализует XT. - person DK.; 27.09.2015
comment
Спасибо за объяснение. Я получаю это сейчас. Я бы сказал, что сделать синтаксис для признаков, равный синтаксису для типов, было бы более эргономичным, но я полагаю, что мне не хватает важных вещей о языке. - person Hernan; 28.09.2015