Использование необъявленной ошибки имени типа с параметризованным трейтом

Я пытаюсь реализовать несколько операций над параметризованным типом (в частности, некоторые общие операции «стека» поверх Vec<T>. Однако я не уверен, как это работает (пока), поэтому вот урезанная версия того, что я в настоящее время борюсь с:

trait Stack<T> {
    fn top(&self) -> Option<T>;
}

impl Stack for Vec<T> {
    fn top<T>(&self) -> Option<T> {
        match self.len() {
            0 => None,
            n => Some(self[n-1])
        }
    }
}

fn main() {
    let mut stack: Vec<f64> = Vec::new();
    stack.push(1324.4);
    println!("{}", stack.top());
}

Приведенное выше не удается скомпилировать (на ржавчине каждую ночь) со следующей ошибкой:

test.rs:6:20: 6:21 error: use of undeclared type name `T`
test.rs:6 impl Stack for Vec<T> {
                             ^
error: aborting due to previous error

person Dave Vogt    schedule 13.02.2015    source источник
comment
Уже есть такой метод, как .top, называемый .last(), который вы можете использовать.   -  person bluss    schedule 14.02.2015
comment
Пожалуйста, не забудьте проголосовать за полезные ответы и отметить ответ как принятый, если он решил вашу проблему! Если ни один из ответов не является приемлемым, рассмотрите возможность оставить комментарий, объясняющий, почему, или отредактируйте свой вопрос, сформулировав проблему по-другому.   -  person Shepmaster    schedule 03.03.2015


Ответы (2)


Это должно работать:

impl<T> Stack<T> for Vec<T> {
    fn top(&self) -> Option<T> {
        match self.len() {
            0 => None,
            n => Some(self[n-1])
        }
    }
}

Вам необходимо сообщить компилятору о параметрах типа Stack, имеющих отношение к реализации.

Но этого недостаточно: исправление этой ошибки вызывает проблему с функцией main, в частности, параметр для println! имеет неправильный тип:

fn main() {
    let mut stack: Vec<f64> = Vec::new();
    stack.push(42.0);
    match stack.pop() {
        None    => println!("empty stack"),
        Some(n) => println!("top: {}", n)
    };
}

Но это исправление, в свою очередь, показывает, что метод top плохо типизирован для этого кода. Один из способов исправить ошибку:

trait Stack<T> {
    fn top(&self) -> Option<&T>;
}

impl<T> Stack<T> for Vec<T> {
    fn top(&self) -> Option<&T> {
        match self.len() {
            0 => None,
            n => Some(&self[n-1])
        }
    }
}

Редактировать:

  • как объяснил @sellibitze в комментариях, причина, по которой это не работает, заключается в том, что из исходного определения Stack компилятор не мог знать, что значения типа T можно копировать или, по крайней мере, клонировать, т.е. что T поддерживает Copy или Clone, и, следовательно, значения не могут быть дублированы (возврат по значению на языке C++) . Использование ссылки решает проблему, поскольку ссылку на T можно скопировать.

  • вместо того, чтобы сопоставлять возвращаемое значение из top, я мог бы использовать тот факт, что Option<T> поддерживает черту Show, когда T реализует ее, что верно для f64. Это означает, что я мог бы просто заменить {} средством форматирования {:?} в вызове макроса println! и оставить остальную часть исходной функции main нетронутой.

Например:

fn main(){
    let mut stack: Vec<f64> = Vec::new();
    stack.push(42.0);
    println!("top: {:?}", stack.top())
}
person didierc    schedule 13.02.2015
comment
Удалите <T> после top. Кроме того, первая показанная вами реализация не должна работать, поскольку для нее требуется T: Copy. - person sellibitze; 14.02.2015
comment
@sellibitze ах да, я удалил его в своих тестах, но он ускользнул от меня, когда я каким-то образом скопировал его обратно, спасибо! Что касается Copy, я не был уверен в причине, почему это не сработало, но исправил это с помощью ссылок. Я немного скептически относился к тому, что это оптимально для таких типов, как числа с плавающей запятой и целые числа. - person didierc; 14.02.2015
comment
компилятор не мог знать, что значения типа T могут быть скопированы, т.е. что T поддерживает трейт Clone — обратите внимание, что типы, которые являются Copyable отличаются от типов, которые Cloneable. - person Shepmaster; 15.02.2015
comment
Правильнее, да. Я присоединился к ответу, который показывает использование границ Copy или Clone, если это то, что хотел ОП. - person Shepmaster; 15.02.2015

Вот две возможные реализации, если вы хотите поддерживать возврат значения вместо ссылки. Обратите внимание, что вы не можете иметь их обоих. В большинстве случаев вы увидите тип с привязкой Clone, поскольку любой тип, относящийся к Copy, также должен реализовывать Clone:

trait Stack<T> {
    fn top(&self) -> Option<T>;
}

// We have values where we duplicate by copying bits naïvely
impl<T> Stack<T> for Vec<T>
    where T: Copy
{
    fn top(&self) -> Option<T> {
        self.last().map(|v| *v)
    }
}

// We have values where we can duplicate them,
// but it might take a function call to do so
impl<T> Stack<T> for Vec<T>
    where T: Clone
{
    fn top(&self) -> Option<T> {
        self.last().map(|v| v.clone())
    }
}

fn main() {
    let stack = vec![1324.4f64];
    println!("{:?}", stack.top());
}
person Shepmaster    schedule 15.02.2015
comment
Спасибо, я уверен, что ОП оценит ваш вклад, как и я. - person didierc; 15.02.2015