2015-09-16 9 views
6

Voglio costruire una funzione che divide una lista in due: una lista che contiene gli elementi della lista originale che soddisfano un determinato predicato, e un'altra che contiene tutti quelli che non la . Qui di seguito è il mio tentativo:Dimensioni non implementate per il tipo Fn

fn split_filter<T: Clone + Sized>(a: &Vec<T>, f: Fn(&T) -> bool) -> (Vec<T>, Vec<T>) { 
    let i: Vec<T> = vec![]; 
    let e: Vec<T> = vec![]; 
    for u in a.iter().cloned() { 
     if f(&u) { 
      i.push(u) 
     } else { 
      e.push(u) 
     } 
    } 

    return (i, e); 
} 

fn main() { 
    let v = vec![10, 40, 30, 20, 60, 50]; 
    println!("{:?}", split_filter(&v, |&a| a % 3 == 0)); 
} 

Tuttavia, ottengo due errori:

error[E0277]: the trait bound `for<'r> std::ops::Fn(&'r T) -> bool + 'static: std::marker::Sized` is not satisfied 
--> src/main.rs:1:47 
    | 
1 | fn split_filter<T: Clone + Sized>(a: &Vec<T>, f: Fn(&T) -> bool) -> (Vec<T>, Vec<T>) { 
    |            ^`for<'r> std::ops::Fn(&'r T) -> bool + 'static` does not have a constant size known at compile-time 
    | 
    = help: the trait `std::marker::Sized` is not implemented for `for<'r> std::ops::Fn(&'r T) -> bool + 'static` 
    = note: all local variables must have a statically known size 

error[E0308]: mismatched types 
    --> src/main.rs:17:39 
    | 
17 |  println!("{:?}", split_filter(&v, |&a| a % 3 == 0)); 
    |          ^^^^^^^^^^^^^^^ expected trait std::ops::Fn, found closure 
    | 
    = note: expected type `for<'r> std::ops::Fn(&'r {integer}) -> bool + 'static` 
       found type `[[email protected]/main.rs:17:39: 17:54]` 

Il secondo errore sembra implicare che una chiusura non è un Fn. Ho provato a utilizzare la sintassi f: |&T| -> bool che ho trovato online da qualche parte, ma non sembra funzionare nell'ultima versione di Rust.

Per quanto riguarda il primo errore, avevo sperato che, per rendere TSized sarebbe fare in modo che la funzione ha una dimensione noto, ma a quanto pare non è così.

+3

Per i lettori futuri, questo metodo è già implementato per gli iteratori, con il nome ['partition'] (http://doc.rust-lang.org/std/iter/trait.Iterator.html#method.partition) . – Shepmaster

risposta

9

Si consiglia di leggere il libro ufficiale di Rust, in particolare the chapter on closures. La tua dichiarazione di funzione è errata; stai specificando che f ha un tipo di tratto nuda, il che è impossibile; questo è esattamente l'errore di Sized. È necessario utilizzare un parametro di tipo generico, invece:

fn split_filter<T: Clone, F>(a: &[T], f: F) -> (Vec<T>, Vec<T>) 
where 
    F: for<'a> Fn(&'a T) -> bool, 

Ho cambiato anche il tipo di a da &Vec<T> a &[T]; non c'è nessuna situazione in cui preferiresti la prima a quest'ultima. &Vec<T> viene forzato automaticamente a &[T] quando necessario. Vedi Why is it discouraged to accept a reference to a String (&String) or Vec (&Vec) as a function argument?

Il secondo errore è strettamente correlato all'errore nella dichiarazione di funzione; la dichiarazione della funzione originale ha specificato un tipo di tratto nuda, ma le chiusure non hanno questo tipo, implementano solo il tratto funzione.

Il programma finale si presenta così:

fn split_filter<T: Clone, F>(a: &[T], f: F) -> (Vec<T>, Vec<T>) 
where 
    F: Fn(&T) -> bool, 
{ 
    let mut i: Vec<T> = vec![]; 
    let mut e: Vec<T> = vec![]; 
    for u in a.iter().cloned() { 
     if f(&u) { 
      i.push(u); 
     } else { 
      e.push(u); 
     } 
    } 

    return (i, e); 
} 

fn main() { 
    let v = vec![10, 40, 30, 20, 60, 50]; 
    println!("{:?}", split_filter(&v, |&a| a % 3 == 0)); 
} 

Prova sul playground.

Problemi correlati