I just started learning rust and I'm creatively trying somethings as I read "the Rust Book".
I know it is possible to create a generic method to get the largest element from an array, like the following:
fn largest<T: PartialOrd Copy> (nums: &[T]) -> T {
let mut largest = nums[0];
for &el in nums {
if el > largest {
largest = el;
}
}
return largest;
}
And calling the main
function like this:
fn main() {
let list: Vec<u32> = vec![1,7,4];
println!("{}", largest(&list)); // 7
}
How would I go doing the same thing but "extending" the array, like this:
fn main() {
let list: Vec<u32> = vec![1,7,4];
println!("{}", list.largest()); // 7
}
I guess the final question is: if it is possible, would it be a bad practice? Why?
I tried something like this, but didn't manage to figure out how to implement the "Largeble" trait returning the T:
pub trait Largeble {
fn largest(&self);
}
impl<T: Copy PartialOrd Display> Largeble for Vec<T> {
fn largest(&self) {
let mut largest = match self.get(0) {
Some(&el) => el,
None => panic!("Non Empty array expected")
};
for &el in self {
if el > largest {
largest = el;
}
}
println!("{}", largest);
// return largest;
}
}
CodePudding user response:
How would I go doing the same thing but "extending" the array
Sure, your code snippet was close, you can create a trait and implement it on types.
pub trait Largeble<T>
where
T: Ord,
{
fn largest(&self) -> Option<&T>;
}
impl<T> Largeble<T> for Vec<T>
where
T: Ord,
{
fn largest(&self) -> Option<&T> {
// Iterator already has a method for getting max which simplifies things
self.iter().max()
}
}
Alternatively, you can make T
an associated type which may be better suited to this example.
You can run this code in the Rust Playground.
Would it be a bad practice? Why?
Nope, it's definitely not bad practise. It is a very common way to developing in Rust and can be very powerful. Your trait needs to be in scope for you to be able to call .largest()
, so it does not pollute anything.
Additionally, if you have multiple methods with the same name from different traits, you can provide a longer syntax to specify the exact trait you want to use: Largest::<u32>::largest(&list)
.
I tried something like this, but didn't manage to figure out how to implement the "Largeble" trait returning the T.
Your code was mostly correct, but your largest method didn't return anything. That's why the trait needs a generic T
, to specify that you will return T
.
CodePudding user response:
You need to make the Largeable
trait return a T
from the Vec<T>
, which you can do with an associated type:
use std::fmt;
pub trait Largeble {
type Output;
fn largest(&self) -> Self::Output;
}
impl<T: Copy PartialOrd fmt::Display> Largeble for Vec<T> {
type Output = T;
fn largest(&self) -> T {
let mut largest = match self.get(0) {
Some(&el) => el,
None => panic!("Non Empty array expected")
};
for &el in self {
if el > largest {
largest = el;
}
}
largest
}
}
println!("{}", vec![1, 2, 3, 2].largest()); // prints "3"
Traits like Largeable
are usually called extension traits, since they extend existing items with new features. Using extension traits to extend items in existing libraries is common in the Rust ecosystem. It's common to suffix the names of extensions with Ext
(so a collection of additional methods for Vec
would be called VecExt
). A popular use of extension traits is the itertools
library, which provides a trait that adds additional useful methods to Iterator
in the standard library.