3

Given any slice, for example:

let words = &["one", "one", "one", "two"];

How to know if all elements are the same?

Even further, if all elements are the same, how to return a reference to the first one?

Essentially, I'm trying to write a function like:

fn are_all_elements_equal<T>(elems: &[T]) -> Option<&T> {
    // ... ?
}
Herohtar
  • 5,347
  • 4
  • 31
  • 41
rodrigocfd
  • 6,450
  • 6
  • 34
  • 68

5 Answers5

5

I think this is a nice use case for subslice patterns:

pub fn are_all_elements_equal<T: PartialEq>(elems: &[T]) -> Option<&T> {
    match elems {
        [head, tail @ ..] => tail.iter().all(|x| x == head).then(|| head),
        [] => None,
    }
}
Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
4

I'd use .all: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.all

First, if the slice is empty, just return None.

Then grab yourself an iterator over the rest of the slice, and use the .all function to check that the element equals the first element that you just grabbed. If that returns true, return your Some(first_element)

cadolphs
  • 9,014
  • 1
  • 24
  • 41
3

As an extension to the answers already posted, you can also make it generic over anything that can be iterated:

pub fn iter_all_eq<T: PartialEq>(iter: impl IntoIterator<Item = T>) -> Option<T> {
    let mut iter = iter.into_iter();
    let first = iter.next()?;
    iter.all(|elem| elem == first).then(|| first)
}

fn main() {
    println!("{:?}", iter_all_eq(&[1, 1, 1]));
    println!("{:?}", iter_all_eq(&[1, 2, 1]));
    println!("{:?}", iter_all_eq(&["abc", "abc", "abc", "abc"]));
}

Playground

Filipe Rodrigues
  • 1,843
  • 2
  • 12
  • 21
1

An elegant way to do this is using tuple_windows from the itertools crate:

use itertools::Itertools;

pub fn are_all_elements_equal<T: Eq>(elems: &[T]) -> Option<&T> {
    elems.iter().tuple_windows().all(|(a, b)| a == b).then(|| &elems[0])
}

Note that this will panic on an empty slice. To handle the empty slice, you need to explicitly return None if elems.is_empty().

user4815162342
  • 141,790
  • 18
  • 296
  • 355
0

It's fairly simple to do using the available built-in functions:

fn check_all<T: Eq>(items: &[T]) -> Option<&T> {
    match items.is_empty() {
        true => None,
        false => items.windows(2).all(|a| a[0] == a[1]).then(|| &items[0])
    }
}

Playground link

  • .windows(2) gives you an iterator containing overlapping pairs of elements.
  • .all(|a| a[0] == a[1]) compares the two elements from each window
  • .then(|| &items[0]) returns an Option containing a reference to the first element if the previous .all() returns true, otherwise it returns None
  • The match items.is_empty() is required because .all() will also return true if the slice is empty, which will result in a panic at items[0]

Note that since the comparison used in .all() could result in the same value being compared to itself, you need to constrain T to Eq, per this answer.

Herohtar
  • 5,347
  • 4
  • 31
  • 41