简体   繁体   中英

How can I specify that a trait function takes an argument implementing the trait?

I created an abstract data type for vectors in metric space but the compiler complains because it doesn't recognize that the implementation as an argument is of this type.

trait MetricPoint {
    fn square_distance(&self, other: &MetricPoint) -> f64;
}

struct RNPoint {
    coordinates: Vec<f64>,
}

impl RNPoint {
    fn new(coordinates: &[f64]) -> RNPoint {
        RNPoint {
            coordinates: coordinates.to_vec(),
        }
    }
}

impl MetricPoint for RNPoint {
    fn square_distance(self: &RNPoint, other: &RNPoint) -> f64 {
        let min_len = self.coordinates.len().min(other.coordinates.len());
        let mut sum = 0.0;
        for i in 0..min_len {
            let diff = self.coordinates[i] - other.coordinates[i];
            sum += diff * diff;
        }
        sum
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn square_distance() {
        let p1 = RNPoint::new(&[0.0, 0.0]);
        let p2 = RNPoint::new(&[1.0, 0.0]);
        let d = p1.square_distance(&p2);
        assert_eq!(d, 1.0)
    }
}

The compiler:

error[E0053]: method `square_distance` has an incompatible type for trait
  --> points/src/lib.rs:19:44
   |
2  |     fn square_distance(&self, other: &MetricPoint) -> f64;
   |                                      ------------ type in trait
...
19 |     fn square_distance(self:&RNPoint,other:&RNPoint)->f64 {
   |                                            ^^^^^^^^ expected trait MetricPoint, found struct `RNPoint`
   |
   = note: expected type `fn(&RNPoint, &dyn MetricPoint) -> f64`
              found type `fn(&RNPoint, &RNPoint) -> f64`

Why it doesn't recognize that an RNPoint is an MetricPoint ?

You are not far from something that compiles and pass your test. Just change:

fn square_distance(&self, other: &MetricPoint) -> f64;

to

fn square_distance(&self, other: &Self) -> f64;

and optionnally

fn square_distance(self: &RNPoint, other: &RNPoint) -> f64

to

fn square_distance(self: &RNPoint, other: &Self) -> f64

By writing Self , you are specifying the type of the current structure. It's more generic and readable.

Thanks. As some said but Hugo more clearly the solution is the self. Here is the final code:

trait Metric {
    fn distance(&self, other: &Self) -> f64;
}

struct RNPoint {
    coordinates: Vec<f64>,
}

impl RNPoint {
    fn new(coordinates: &[f64]) -> RNPoint {
        RNPoint {
            coordinates: coordinates.to_vec(),
        }
    }
}

impl Metric for RNPoint {

    fn distance(&self, other: &Self) -> f64 {
        let sum:f64=self.coordinates.iter()
            .zip(other.coordinates.iter())
            .map(|(&c1, &c2)| {
                let diff = c1 - c2;
                diff * diff
            })
            .sum();
         sum.sqrt()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_distance() {
        let p1 = RNPoint::new(&[0.0, 0.0]);
        let p2 = RNPoint::new(&[1.0, 0.0]);
        let d = p1.distance(&p2);
        assert_eq!(d, 1.0)
    }
}

About the abstract class I wrote Abstract Data Type but stack overflow changed it and I didn't notice. Sorry about

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM