3

I want to write a function that would reverse a string and return a reference to the returned string. I thought I would be able to specify that the returned reference has the same lifetime as the provided reference.

fn reverse_string<'a>(input: &'a str) -> &'a str {
    let res: &'a str = input.chars().rev().collect().as_slice();
    res
}

This was my attempt, and I receive the error:

error[E0282]: type annotations needed
 --> src/lib.rs:2:24
  |
2 |     let res: &'a str = input.chars().rev().collect().as_slice();
  |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for `B`
  |
  = note: type must be known at this point

I thought the type specification would resolve this. Lifetimes are a bit confusing to me, so I was hoping someone may be able to explain how to do this properly, or if I'm completely off base to begin with.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Alex P
  • 530
  • 1
  • 6
  • 13

2 Answers2

7

The lifetime issue is similar to The lifetime of &str makes comparison hard: if you create a new object, it's a new object, uncorrelated from the old one.

Therefore, you have two choices:

  • either create a completely new object: input.chars().rev().collect() for example
  • or reverse the input in-place, which requires it to be mut of course

The former looks easy:

fn reverse_new(input: &str) -> String {
    input.chars().rev().collect()
}

however the String so created might be "flawed", because char (the intermediate type) are just Unicode code points not graphemes. As an example of Combining Characters imagine that you have [e, ́, o] in the original string, the ́ is attached to the e: éo, but after reversal you get [o, ́, e] where ́ is attached to the o: óe.

The latter is severely complicated by the fact that even ignoring graphemes issues, reversing UTF-8 in-place requires being UTF-8 aware to avoid splitting codepoints. Of course, one could simply create a reversed String and copy it into the input...

EDIT: I had initially thought of collecting into an intermediary Vec, but @AlexP realized it was unnecessary and one could collect directly into a String.

Community
  • 1
  • 1
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Thanks for the help! However, when trying to compile the above snippet, using rust 0.12.0-dev, I receive the error "the type of this value needs to be known in this context," with the portion "v.as_slice()" indicated. – Alex P Nov 13 '14 at 19:46
  • (Whoops, missed edit window) And I was wondering what the lifetime of the returned reference would be? I'm not actually sure how returning references works with regards to borrowing, etc., and I couldn't find anything on it in the documentation. – Alex P Nov 13 '14 at 20:15
  • @AlexP: You cannot return a reference from a function if the referent was created in the function, because the value would be destroyed when the function exits and the reference would be dangling. By returning a `String`, you return a value that owns the string's contents, so there is no borrowing involved. – Francis Gagné Nov 14 '14 at 03:36
  • Thanks @FrancisGagné, really appreciate the help. :) Managed to iron out my problems. – Alex P Nov 14 '14 at 06:16
  • @AlexP: Did you need an annotation for `collect` ? If required, you can use either `let v: Vec = ...;` or `collect::>()`. – Matthieu M. Nov 14 '14 at 07:17
  • @MatthieuM. This was the final function I ended up using. It seems to work as I need it, but please let me know if anything is wrong with it that I should be aware of. fn reverse_string(input: &str) -> String { input.chars().rev().collect() } – Alex P Nov 14 '14 at 08:09
  • @AlexP: Ah nice, even better than using a temporary vector! The only flaw, as I mentioned, is that Unicode contains [Combining Characters](http://en.wikipedia.org/wiki/Combining_character). So if you have `e` followed by ` ́` followed by `o` in the original string, the ` ́` is attached to the `e`: `éo`, but after reversal it is attached to the `o`: `óe`. – Matthieu M. Nov 14 '14 at 09:14
0

The function that ended up functioning as intended:

fn reverse_string(input: &str) -> String {
    input.chars().rev().collect()
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366