64

What is wrong with this:

fn main() {
    let word: &str = "lowks";
    assert_eq!(word.chars().rev(), "skwol");
}

I get an error like this:

error[E0369]: binary operation `==` cannot be applied to type `std::iter::Rev<std::str::Chars<'_>>`
 --> src/main.rs:4:5
  |
4 |     assert_eq!(word.chars().rev(), "skwol");
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: an implementation of `std::cmp::PartialEq` might be missing for `std::iter::Rev<std::str::Chars<'_>>`
  = note: this error originates in a macro outside of the current crate

What is the correct way to do this?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Muhammad Lukman Low
  • 8,177
  • 11
  • 44
  • 54

3 Answers3

97

Since, as @DK. suggested, .graphemes() isn't available on &str in stable, you might as well just do what @huon suggested in the comments:

fn main() {
    let foo = "palimpsest";
    println!("{}", foo.chars().rev().collect::<String>());
}
bright-star
  • 6,016
  • 6
  • 42
  • 81
  • 10
    As mentioned in DK.'s answer, **this can be wrong**. For example, it [completely fails with `"Åström"`](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0a246b78ace33fc66368f8c9e81320fb) – Shepmaster Jan 13 '21 at 14:40
  • 3
    @Shepmaster, Looks like `"Åström"` works in the above playground link with `chars()`. At least the output I see is `m̈orts̊A`. Not sure if stable was recently updated to support this. – MilesF May 25 '21 at 19:49
  • 2
    @MilesF `Ås` vs `s̊A` / `öm` vs `m̈o` – Shepmaster May 25 '21 at 20:14
  • Good point. In the playground output window, things appear to be better aligned. https://i.ibb.co/31rNjXT/chars.png – MilesF May 25 '21 at 20:29
  • `rev` can't be called on `Chars` (anymore?) – theonlygusti Mar 22 '23 at 19:40
68

The first, and most fundamental, problem is that this isn't how you reverse a Unicode string. You are reversing the order of the code points, where you want to reverse the order of graphemes. There may be other issues with this that I'm not aware of. Text is hard.

The second issue is pointed out by the compiler: you are trying to compare a string literal to a char iterator. chars and rev don't produce new strings, they produce lazy sequences, as with iterators in general. The following works:

/*!
Add the following to your `Cargo.toml`:

```cargo
[dependencies]
unicode-segmentation = "0.1.2"
```
*/
extern crate unicode_segmentation;
use unicode_segmentation::UnicodeSegmentation;

fn main() {
    let word: &str = "loẅks";
    let drow: String = word
        // Split the string into an Iterator of &strs, where each element is an
        // extended grapheme cluster.
        .graphemes(true)
        // Reverse the order of the grapheme iterator.
        .rev()
        // Collect all the chars into a new owned String.
        .collect();

    assert_eq!(drow, "skẅol");

    // Print it out to be sure.
    println!("drow = `{}`", drow);
}

Note that graphemes used to be in the standard library as an unstable method, so the above will break with sufficiently old versions of Rust. In that case, you need to use UnicodeSegmentation::graphemes(s, true) instead.

rofrol
  • 14,438
  • 7
  • 79
  • 77
DK.
  • 55,277
  • 5
  • 189
  • 162
  • 22
    I think you can just `.rev().collect()`, since `String` implements `FromIterator<&str>`. Also, fwiw, I think the *actual* most fundamental problem is misunderstanding iterators, strings and types in general (understandable, many languages aren't so "pedantic"), not the somewhat finer points of unicode correctness. – huon Jan 17 '15 at 07:55
  • 1
    @dbaupp: I'd argue a problem that is independent of the implementation language is *more* fundamental than one which is specific to a particular language. :D But it's nice to know `String` supports `FromIterator<&str>`. A slight pity it doesn't pre-allocate the storage, but you can't always get what you want... – DK. Jan 17 '15 at 08:54
  • 1
    Eh, the question is about why a certain piece of code doesn't compile in a particular language, not why it is giving unexpected output (i.e. a language-independent problem with the algorithm), so the fundamental problem for the question itself is the Rust-specific type errors. It's definitely good to mention that unicode is hard, though. – huon Jan 17 '15 at 09:22
0

If you are just dealing with ASCII characters, you can make the reversal in place with the unstable reverse function for slices.
It is doing something like that:

fn main() {
    let mut slice = *b"lowks";
    let end = slice.len() - 1;
    for i in 0..end / 2 {
        slice.swap(i, end - i);
    }
    assert_eq!(std::str::from_utf8(&slice).unwrap(), "skwol");
}

Playground

Kaplan
  • 2,572
  • 13
  • 14