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 reader = io::stdin();
let mut input = String::new();
print!("{}", prompt);
stdout.lock().flush().unwrap();
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 reader = io::stdin();
let mut input = String::new();
print!("{}", prompt);
stdout.lock().flush().unwrap();
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:
error[E0515]: cannot return value referencing local variable `input`
--> src/lib.rs:13:5
|
13 | Ok(Cow::Borrowed(input.trim_right_matches(|c| {
| ^ ----- `input` is borrowed here
| _____|
| |
14 | | if !trimmed && c == '\n' {
15 | | trimmed = true;
16 | | true
... |
20 | | }
21 | | })))
| |________^ returns a value referencing data owned by the current function
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'
).
The canonical way to strip end-of-line (EOL) characters is to use the string rstrip() method removing any trailing \r or \n. Here are examples for Mac, Windows, and Unix EOL characters. Using '\r\n' as the parameter to rstrip means that it will strip out any trailing combination of '\r' or '\n'.
The rstrip() method removes any trailing character at the end of the string. By using this method, we can remove newlines in the provided string value.
A trailing new line is a '\n' character at the end of a line like that in Python strings.
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); }
A cross-platform way of stripping a single trailing newline without reallocating the string is this:
fn trim_newline(s: &mut String) {
if s.ends_with('\n') {
s.pop();
if s.ends_with('\r') {
s.pop();
}
}
}
This will strip either "\n"
or "\r\n"
from the end of the string, but no additional whitespace.
strip_suffix
This removes one trailing \r\n
or \n
:
fn strip_trailing_newline(input: &str) -> &str {
input
.strip_suffix("\r\n")
.or(input.strip_suffix("\n"))
.unwrap_or(input)
}
If there are multiple newlines, only the last one is stripped off.
If there is no newline at the end of the string, the string is unchanged.
Some tests:
#[test]
fn strip_newline_works(){
assert_eq!(strip_trailing_newline("Test0\r\n\r\n"), "Test0\r\n");
assert_eq!(strip_trailing_newline("Test1\r\n"), "Test1");
assert_eq!(strip_trailing_newline("Test2\n"), "Test2");
assert_eq!(strip_trailing_newline("Test3"), "Test3");
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With