-1

I want to use function pointers to point to either of these 2 functions below. Both functions work as expected, when called directly.

When I want to use pointers, their types are not compatible, even though they have the same parameters a: &'a [T], b: &'a [T] and both return f64.

use ndarray::linalg::Dot;
use ndarray::{ArrayView, ArrayBase, RawData};

#[inline]
pub fn euclidean_distance<'a, T>(a: &'a [T], b: &'a [T]) -> f64
where
    f64: From<T>,
    T: Copy,
{
    a.iter()
        .zip(b.iter())
        .fold(0f64, |acc, (&x, &y)| {
            acc + (f64::from(x) - f64::from(y)).powi(2)
        })
        .sqrt()
}

#[inline]
pub fn cosine<'a, T>(a: &'a [T], b: &'a [T]) -> f64
where
    T: Copy,
    ArrayView<'a, T, Ix1>: Dot<ArrayView<'a, T, Ix1>, Output = f64>,
{
    let x = ArrayView::from(a);
    let y = ArrayView::from(b);
    x.dot(&y) / (x.dot(&x) * y.dot(&y)).sqrt()
}

When, I want to use pointers to these function

pub struct Model<'a, T>
where
    T: Copy,
    f64: From<T>,
{
    /// Epsilon value - maximum distance between points in a cluster
    pub eps: f64,
    /// Minimum number of points in a cluster
    pub mpt: usize,

    distance: fn(a: &'a [T], b: &'a [T]) -> f64,
    c: Vec<Classification>,
    v: Vec<bool>,
}

impl<'a, T> Model<'a, T>
where
    T: Copy,
    f64: From<T>,
{
    /// Create a new `Model` with a set of parameters
    ///
    /// # Arguments
    /// * `eps` - maximum distance between datapoints within a cluster
    /// * `min_points` - minimum number of datapoints to make a cluster
    pub fn new(eps: f64, min_points: usize, mode: &str) -> Model<T> {
        Model {
            eps,
            mpt: min_points,
            c: Vec::new(),
            v: Vec::new(),
            distance: match mode {
                "euclidean" => euclidean_distance,
                "cosine" => cosine,
                _ => panic!("Unknown Mode {:?}", mode),
            },
        }
    }
}

I get the following error: "expected fn pointer, found fn item". I don't understand why the functions have different incompatible types.

error[E0308]: `match` arms have incompatible types
   --> src/dbscan.rs:115:29
    |
113 |               distance: match mode {
    |  _______________________-
114 | |                 "euclidean" => euclidean_distance,
    | |                                ------------------ this is found to be of type `fn(&[T], &[T]) -> f64`
115 | |                 "cosine" => cosine,
    | |                             ^^^^^^ expected fn pointer, found fn item
116 | |                 _ => panic!("Unknown Mode {:?}", mode),
117 | |             },
    | |_____________- `match` arms have incompatible types
    |
    = note: expected fn pointer `fn(&[T], &[T]) -> _`
                  found fn item `fn(&[f64], &[f64]) -> _ {dbscan::cosine::<'_, f64>}`
Luxbit
  • 172
  • 1
  • 2
  • 11
  • I believe you want a generic struct with a `T: Fn(...) -> ....` – Victor Jul 19 '22 at 12:13
  • Could you expand on this? I don't understand what you mean. – Luxbit Jul 19 '22 at 12:39
  • Does this answer your question? ["Expected fn item, found a different fn item" when working with function pointers](https://stackoverflow.com/questions/27895946/expected-fn-item-found-a-different-fn-item-when-working-with-function-pointer) – Chayim Friedman Jul 19 '22 at 12:44
  • It works if you explicitly cast the functions. You don't even need to specify the type, you just need the cast itself: `"euclidean" => euclidean_distance as _, "cosine" => cosine as _,` ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=963ad73cb49b1ae802e285cb0175f3de)) – Jmb Jul 19 '22 at 14:03

1 Answers1

0

It just required a couple of minor lifetime tweaks and there was a where clause missing in impl Model:

use ndarray::linalg::Dot;
use ndarray::{ArrayView, Ix1};

struct Classification;

#[inline]
pub fn euclidean_distance<T>(a: &[T], b: &[T]) -> f64
where
    f64: From<T>,
    T: Copy,
{
    a.iter()
        .zip(b.iter())
        .fold(0f64, |acc, (&x, &y)| {
            acc + (f64::from(x) - f64::from(y)).powi(2)
        })
        .sqrt()
}

#[inline]
pub fn cosine<T>(a: &[T], b: &[T]) -> f64
where
    T: Copy,
    for<'a> ArrayView<'a, T, Ix1>: Dot<ArrayView<'a, T, Ix1>, Output = f64>,
{
    let x = ArrayView::from(a);
    let y = ArrayView::from(b);
    x.dot(&y) / (x.dot(&x) * y.dot(&y)).sqrt()
}

pub struct Model<T>
where
    T: Copy,
    f64: From<T>,
{
    /// Epsilon value - maximum distance between points in a cluster
    pub eps: f64,
    /// Minimum number of points in a cluster
    pub mpt: usize,

    distance: fn(a: &[T], b: &[T]) -> f64,
    c: Vec<Classification>,
    v: Vec<bool>,
}

impl<T> Model<T>
where
    T: Copy,
    f64: From<T>,
    for<'a> ArrayView<'a, T, Ix1>: Dot<ArrayView<'a, T, Ix1>, Output = f64>,
{
    /// Create a new `Model` with a set of parameters
    ///
    /// # Arguments
    /// * `eps` - maximum distance between datapoints within a cluster
    /// * `min_points` - minimum number of datapoints to make a cluster
    pub fn new(eps: f64, min_points: usize, mode: &str) -> Model<T> {
        Model {
            eps,
            mpt: min_points,
            c: Vec::new(),
            v: Vec::new(),
            distance: match mode {
                "euclidean" => euclidean_distance,
                "cosine" => cosine,
                _ => panic!("Unknown Mode {:?}", mode),
            },
        }
    }
}
Finomnis
  • 18,094
  • 1
  • 20
  • 27