Грешка при използване на недекларирано име на тип с параметризирана черта

Опитвам се да внедря няколко операции върху параметризиран тип (по-конкретно, някои често срещани операции „стек“ върху 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());
}

Горното не успява да се компилира (на rust nightly) със следната грешка:

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, не бях сигурен за причината, поради която не работи, но използването на препратки го поправи. Бях малко скептичен относно това, че това е оптимално в случай на типове като float и int. - person didierc; 14.02.2015
comment
компилаторът не може да знае, че стойностите от тип T могат да се копират — т.е. че T поддържа чертата Clone - имайте предвид, че типове, които са Copyable се различават от типове, които са Cloneспособни. - person Shepmaster; 15.02.2015
comment
По-правилно, да. Включих се с отговор, който показва използването на границите Copy или Clone, ако това е искането на OP. - 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
Благодаря, сигурен съм, че OP ще оцени приноса ви, както и аз. - person didierc; 15.02.2015