Remove single trailing newline from String without

2019-02-21 23:28发布

I have written a function to prompt for input and return the result. In this version the returned string includes a trailing newline from the user. I would like to return the input with that newline (and just that newline) removed:

fn read_with_prompt(prompt: &str) -> io::Result<String> {
    let stdout = io::stdout();
    let mut reader = io::stdin();
    let mut input = String::new();
    print!("{}", prompt);
    stdout.lock().flush().unwrap();
    try!(reader.read_line(&mut input));

    // TODO: Remove trailing newline if present
    Ok(input)
}

The reason for only removing the single trailing newline is that this function will also be used to prompt for a password (with appropriate use of termios to stop echoing) and if someone's password has trailing whitespace this should be preserved.

After much fussing about how to actually remove a single newline at the end of a string I ended up using trim_right_matches. However that returns a &str. I tried using Cow to deal with this but the error still says that the input variable doesn't live long enough.

fn read_with_prompt<'a>(prompt: &str) -> io::Result<Cow<'a, str>> {
    let stdout = io::stdout();
    let mut reader = io::stdin();
    let mut input = String::new();
    print!("{}", prompt);
    stdout.lock().flush().unwrap();
    try!(reader.read_line(&mut input));

    let mut trimmed = false;
    Ok(Cow::Borrowed(input.trim_right_matches(|c| {
        if !trimmed && c == '\n' {
            trimmed = true;
            true
        }
        else {
            false
        }
    })))
}

Error:

src/main.rs:613:22: 613:27 error: `input` does not live long enough
src/main.rs:613     Ok(Cow::Borrowed(input.trim_right_matches(|c| {
                                     ^~~~~
src/main.rs:604:79: 622:2 note: reference must be valid for the lifetime 'a as
 defined on the block at 604:78...
src/main.rs:604 fn read_with_prompt<'a, S: AsRef<str>>(prompt: S) -> io::Resul
t<Cow<'a, str>> {
src/main.rs:605     let stdout = io::stdout();
src/main.rs:606     let mut reader = io::stdin();
src/main.rs:607     let mut input = String::new();
src/main.rs:608     print!("{}", prompt.as_ref());
src/main.rs:609     stdout.lock().flush().unwrap();
                ...
src/main.rs:607:35: 622:2 note: ...but borrowed value is only valid for the bl
ock suffix following statement 2 at 607:34
src/main.rs:607     let mut input = String::new();
src/main.rs:608     print!("{}", prompt.as_ref());
src/main.rs:609     stdout.lock().flush().unwrap();
src/main.rs:610     try!(reader.read_line(&mut input));
src/main.rs:611
src/main.rs:612     let mut trimmed = false;
                ...

Based on previous questions along these lines it seems this is not possible. Is the only option to allocate a new string that has the trailing newline removed? It seems there should be a way to trim the string without copying it (in C you'd just replace the '\n' with '\0').

标签: string rust
2条回答
来,给爷笑一个
2楼-- · 2019-02-21 23:52

You can use String::pop or String::truncate:

fn main() {
    let mut s = "hello\n".to_string();
    s.pop();
    assert_eq!("hello", &s);

    let mut s = "hello\n".to_string();
    let len = s.len();
    s.truncate(len - 1);
    assert_eq!("hello", &s);
}
查看更多
萌系小妹纸
3楼-- · 2019-02-22 00:08

A more generic solution than the accepted one, that works with any kind of line ending:

fn main() {
    let mut s = "hello\r\n".to_string();
    let len_withoutcrlf = s.trim_right().len();
    s.truncate(len_withoutcrlf);
    assert_eq!("hello", &s);
}
查看更多
登录 后发表回答