Umbrella method call for methods in different Types

Dear rust people,

I'm a novice of Rust and trying to test the limits of my understanding of the language, so bear with me if this code is very stupid and/or non-idiomatic within Rust.

Coming from a science background and working with Python, I'm used to methods like .sort() working out-of-the-box on a dynamic array containing numbers. Now while working in Rust I've come to learn there are inherent difficulties sorting mutable vectors containing floats. While I found that one can use .sort() for Vec<i32> and the like, and .sort_by(f64::total_cmp) for Vec, I have attempted to make a generic do_sort() method which works on vectors of any type of number Type:

  6 // Custom trait for all types that can be sorted with .sort()
  7 pub trait CanSort: std::cmp::Ord {}
  8
  9 impl CanSort for i32 {}
 10 impl CanSort for u32 {}
 11 impl CanSort for i64 {}
 12 impl CanSort for u64 {}
 13 //impl CanSort for char {}  // you rarely work with Vec<char>, instead it's commonly Vec<&str>
 14 //impl CanSort for &str {}  // may not want to accept &str as this can fail during conversion to f64, and requires extra code
 15
 16 // Let's first implement a trait that will be common for all our array slices with numbers:
 17 pub trait CustomSort {
 18     fn do_sort(&mut self);
 19 }
 20
 21 // All Types which implement Ord use self.sort()
 22 impl<T: CanSort> CustomSort for Vec<T> {
 23     fn do_sort(&mut self) {
 24         self.sort();
 25     }
 26 }
 27 // For f64
 28 impl CustomSort for Vec<f64> {  // Do we need to change this to implement f64 instead of Vec<f64>?
 29     fn do_sort(&mut self) {
 30         self.sort_by(f64::total_cmp);
 31     }
 32 }
 33 // For f32
 34 impl CustomSort for Vec<f32> {
 35     fn do_sort(&mut self) {
 36         self.sort_by(f32::total_cmp);
 37     }
 38 }
 39

This code does what i want it to: I can create a: Vec<f64> = vec!([2.2, 3.3, 1.1]) and run a.do_sort() and it works as expected. I can also use this function directly as a library:

use crate::customsort::{CustomSort};
pub mod customsort;

fn main() {
    let list_f64: [f64; 3] = [2.2, 3.3, 1.1];
    let mut f64v: Vec<f64> = Vec::from(list_f64);

    let list_i64: [i64; 3] = [2, 3, 1];
    let mut i64v: Vec<i64> = Vec::from(list_i64);

    f64v.do_sort();
    i64v.do_sort();

    assert_eq!(f64v, Vec::from([1.1_f64, 2.2, 3.3]));
    assert_eq!(i64v, Vec::from([1_i64, 2, 3]));
}

But I would like to be able to use .do_sort() within a function. This is where I encounter a lot of problems:

use crate::customsort::{CustomSort, CanSort};
pub mod customsort;

fn main() {
    let mut f64v: Vec<f64> = Vec::from([2.2, 3.3, 1.1]);

    test_do_sort(&mut f64v);
}

fn test_do_sort<T>(v: &mut Vec<T>)
where
    T: CustomSort + CanSort + Copy + Ord + std::fmt::Debug
{
    v.do_sort();
}

with cargo run this produces the error:

error[E0277]: the trait bound `f64: CustomSort` is not satisfied
  --> src/main.rs:77:18
   |
77 |     test_do_sort(&mut f64v);
   |     ------------ ^^^^^^^^^ the trait `CustomSort` is not implemented for `f64`
   |     |
   |     required by a bound introduced by this call
   |
   = help: the following other types implement trait `CustomSort`:
             Vec<T>
             Vec<f32>
             Vec<f64>
note: required by a bound in `test_do_sort`
  --> src/main.rs:82:8
   |
80 | fn test_do_sort<T>(v: &mut Vec<T>)
   |    ------------ required by a bound in this function
81 | where
82 |     T: CustomSort + CanSort + Copy + Ord + std::fmt::Debug
   |        ^^^^^^^^^^ required by this bound in `test_do_sort`

error[E0277]: the trait bound `f64: CanSort` is not satisfied
  --> src/main.rs:77:18
   |
77 |     test_do_sort(&mut f64v);
   |     ------------ ^^^^^^^^^ the trait `CanSort` is not implemented for `f64`
   |     |
   |     required by a bound introduced by this call
   |
   = help: the following other types implement trait `CanSort`:
             i32
             i64
             u32
             u64
note: required by a bound in `test_do_sort`
  --> src/main.rs:82:21
   |
80 | fn test_do_sort<T>(v: &mut Vec<T>)
   |    ------------ required by a bound in this function
81 | where
82 |     T: CustomSort + CanSort + Copy + Ord + std::fmt::Debug
   |                     ^^^^^^^ required by this bound in `test_do_sort`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `common_collections_samples` (bin "common_collections_samples") due to 2 previous errors

While I understand that Rust complains that I'm trying to create a function which accepts Types which have both traits CustomSort and CanSort, which have no overlap, I'm struggling to figure out a way to allow all Types that have either of these traits, as both provide an implementation for do_sort() for the different Types. I can't add f32 or f64 to CanSort, as these are not members of trait Ord and can't be sorted with the typical .sort().

I've been stuck on this for more than a day, and would be very happy for any help or pointers to a better way of achieving the same goal (without relying on a separate Crate, since I want to understand better how to do this myself, not just get a finished implementation of .sort()).

Many thanks
// André

You implemented CustomSort for Vec<U>, but your function takes &mut Vec<T> where T: CustomSort, so you are actually requiring &mut Vec<Vec<U>>.

If you changed the function to take &mut T and removed the other bounds, it would work:

fn test_do_sort<T>(v: &mut T)
where
    T: CustomSort

This might not be what you want, though. Another option would be to change CanSort to be an Ord-like trait, and use that in CustomSort:

On an unrelated note, since sorting doesn't require the capabilities of a Vec[1], you can instead implement CustomSort for slices[2] instead. That is strictly more useful, because Vec's can be used as slices[3]:

impl<T: CanSort> CustomSort for [T] { /* ... */ }

Also, you can use a macro to reduce boilerplate:


  1. doesn't change the length, only the elements themselves ↩︎

  2. [T] ↩︎

  3. because of Deref-coercion ↩︎

3 Likes

You can also simply do this:

fn test_do_sort<T>(v: &mut Vec<T>) 
where
    Vec<T>: CustomSort
{ … }

if you actually want to restrict the function to Vecs (for example because you want to add/remove elements). But agreed that for more flexibility you should impl CustomSort for slices rather than Vecs.

1 Like

Dear FZs and Johannes,

Thanks a lot for your feedback! Johannes suggestion indeed made everything "click" for me regarding my implementation, and now it appears to work as I expect it to.

I'm very grateful for the detailed response from FZs, and this is for sure a better way of implementing my solution. I clearly need to become more comfortable with working with the standard lib, and to decipher the compiler log. Many thanks to you both!

Cheers

2 Likes