Append new line character to Hyper Body struct - rust

I am trying to modify the response that gets returned back from the echo example when using hyper. Their code sample is here.
The only thing that is different in my code is that I am trying to append a new line character to the end of my collection
(&Method::POST, "/echo/uppercase") => {
let newLine = "\n".as_bytes().to_vec();
let m = req.into_body()
.map_ok(|c| {
c.iter()
.map(|byte| byte.to_ascii_uppercase())
.collect::<Vec<u8>>()
.append(&mut newLine) <-- this line
});
*response.body_mut() = Body::wrap_stream(m);
},
The compiler is returning
*response.body_mut() = Body::wrap_stream(m);
^ the trait `std::convert::From<()>` is not implemented for `bytes::bytes::Bytes`
Does the append modify the type of the collection so that it is no longer considered a Future stream for the wrap_stream method?
Also is this the preferred way to append things to a Body struct in hyper?

The closure passed to map_ok() returns (), not the Vec<u8> you are expecting.
This is because Vec::append(), which you use to append the newline, returns (), not the modified Vec.
Simple solution: modify the closure to return the Vec<u8>:
|c| {
let mut v = c.iter()
.map(|byte| byte.to_ascii_uppercase())
.collect::<Vec<u8>>();
v.append(&mut newline);
v
}
Since a newline is a one-byte character, it might make more sense to just add it to the Vec directly:
v.push(b'\n');
This avoids the unnecessary allocation of the newline Vec for every request.

Related

How do i split a string twice in rust?

i need split String "fooo:3333#baaar:22222"
Firstly by # secondary by :
and result must be <Vec<Vec<&str, i64>>>
for the first step (split by #) i came up with
.split('#').collect::<Vec<&str>>()
but I can't think of a solution for the second step
A Vec<&str, i64> is not a thing, so I assume you meant (&str, i64)
You can create that by splitting first, then mapping over the chunks.
let v = s
.split('#') // split first time
// "map" over the chunks and only take those where
// the conversion to i64 worked
.filter_map(|c| {
// split once returns an `Option<(&str, &str)>`
c.split_once(':')
// so we use `and_then` and return another `Option`
// when the conversion worked (`.ok()` converts the `Result` to an `Option`)
.and_then(|(l, r)| r.parse().ok().map(|r| (l, r)))
})
.collect::<Vec<(&str, i64)>>();
References:
https://doc.rust-lang.org/std/primitive.str.html#method.split
https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map
https://doc.rust-lang.org/core/primitive.str.html#method.split_once
https://doc.rust-lang.org/core/option/enum.Option.html#method.and_then
https://doc.rust-lang.org/std/primitive.str.html#method.parse
https://doc.rust-lang.org/std/result/enum.Result.html#method.ok
You can call a closure on each element of an iterator returned by the first split and split inside closure and push values to the vector.
let mut out = Vec::new();
s.split('#').for_each(|x| out.push(x.split(":").collect::<Vec<&str>>()));

Unwrapping a skipped Chars iterator

Many iterator methods is Rust generate iterators wrapped up in iterators. One such case is the skip method, that skips the given number of elements and yields the remaining ones wrapped in the Skip struct that implements the Iterator trait.
I would like to read a file line by line, and sometimes skip the n first characters of a line. I figured that using Iterator.skip would work, but now I'm stuck figuring out how I can actually unwrap the yielded Chars iterator so I could materialize the remaining &str with chars.as_str().
What is the idiomatic way of unwrapping an iterator in rust? The call chain
let line: &String = ...;
let remaining = line.chars().skip(n).as_str().trim();
raises the error
error[E0599]: no method named `as_str` found for struct `std::iter::Skip<std::str::Chars<'_>>` in the current scope
--> src/parser/directive_parsers.rs:367:63
|
367 | let option_val = line.chars().skip(option_val_indent).as_str().trim();
| ^^^^^^ method not found in `std::iter::Skip<std::str::Chars<'_>>`
error: aborting due to previous error
You can retrieve the start byte index of the nth character using the nth() method on the char_indices() iterator on the string. Once you have this byte index, you can use it to get a subslice of the original string:
let line = "This is a line.";
let index = line.char_indices().nth(n).unwrap().0;
let remaining = &line[index..];
Rather than iterate over chars, you can use char_indices to find the exact point at which to take a slice from the string, ensuring that you don't index into the middle of a multi-byte character. This will save on an allocation for each line in the iterator:
input
.iter()
.map(|line| {
let n = 2; // get n from somewhere?
let (index, _) = line.char_indices().nth(n).unwrap();// better error handling
&line[index..]
})

How do I create an iterator of lines from a file that have been split into pieces?

I have a file that I need to read line-by-line and break into two sentences separated by a "=". I am trying to use iterators, but I can't find how to use it properly within split. The documentation says that std::str::Split implements the trait, but I'm still clueless how to use it.
use std::{
fs::File,
io::{prelude::*, BufReader},
};
fn example(path: &str) {
for line in BufReader::new(File::open(path).expect("Failed at opening file.")).lines() {
let words = line.unwrap().split("="); //need to make this an iterable
}
}
How can I use a trait I know is already implemented into something like split?
As #Mateen commented, split already returns an iterable. To fix the lifetime problems, save the value returned by unwrap() into a variable before calling split.
I'll try to explain the lifetime issue here.
First it really helps to look at the function signatures.
pub fn unwrap(self) -> T
pub fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P>
unwrap is pretty simple, it takes ownership of itself and returns the inner value.
split looks scary, but it's not too difficult, 'a is just a name for the lifetime, and it just states how long the return value can be used for. In this case it means that both the input arguments must live at least as long as the return value.
// Takes by reference, no ownership change
// v
pub fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P>
// ^ ^ ^ ^
// | |--|---| |
// This just declares a name. | |
// | |
// Both of these values must last longer than -----|
This is because split doesn't copy any of the string, it just points to the position on the original string where the split takes place. If the original string for some reason was dropped, the Split will not point to invalid data.
A variable's lifetime (unless the ownership is passed to something else) lasts till it is out of scope, this is either at the closing } if it is named (e.g. with let) or it is at the end of line / ;
That's why there is a lifetime problem in your code:
for line in std::io::BufReader::new(std::fs::File::open(path).expect("Failed at opening file.")).lines() {
let words = line
.unwrap() // <--- Unwrap consumes `line`, `line` can not be used after calling unwrap(),
.split("=") // Passed unwrap()'s output to split as a reference
; //<-- end of line, unwrap()'s output is dropped due to it not being saved to a variable, the result of split now points to nothing, so the compiler complains.
}
Solutions
Saving the return value of unwrap()
for line in std::io::BufReader::new(std::fs::File::open("abc").expect("Failed at opening file.")).lines() {
let words = line.unwrap();
let words_split = words.split("=");
} // <--- `word`'s lifetime ends here, but there is no lifetime issues since `words_split` also ends here.
You can rename words_split to words to shadow the original variable to not clutter variable names if you want, this also doesn't cause an issue since shadowed variables are not dropped immediately, but at the end of its original scope.
Or
Rather than having a iterator of type str, all of which are just fancy pointers to the original string, you can copy each slice out to it's own string, removing the reliance on keeping the original string in scope.
There is almost certainly no reason to do this in your case, since copying each slice takes more processing power and more memory, but rust gives you this control.
let words = line
.unwrap()
.split("=")
.map(|piece|
piece.to_owned() // <--- This copies all the characters in the str into it's own String.
).collect::<Vec<String>>()
; // <--- unwrap()'s output dropped here, but it doesn't matter since the pieces no longer points to the original line string.
let words_iterator = words.iter();
collect gives you the error cannot infer type because you didn't state what you wanted to collect into, either use the turbofish syntax above, or state it on words i.e. let words: Vec<String> = ...
You have to call collect because map doesn't do anything unless you use it, but that's out of the scope of this answer.

How can I append a char or &str to a String without first converting it to String?

I am attempting to write a lexer for fun, however something keeps bothering me.
let mut chars: Vec<char> = Vec::new();
let mut contents = String::new();
let mut tokens: Vec<&String> = Vec::new();
let mut append = String::new();
//--snip--
for _char in chars {
append += &_char.to_string();
append = append.trim().to_string();
if append.contains("print") {
println!("print found at: \n{}", append);
append = "".to_string();
}
}
Any time I want to do something as simple as append a &str to a String I have to convert it using .to_string, String::from(), .to_owned, etc.
Is there something I am doing wrong, so that I don't have to constantly do this, or is this the primary way of appending?
If you're trying to do something with a type, check the documentation. From the documentation for String:
push: "Appends the given char to the end of this String."
push_str: "Appends a given string slice onto the end of this String."
It's important to understand the differences between String and &str, and why different methods accept and return each of them.
A &str or &mut str are usually preferred in function arguments and return types. That's because they are just pointers to data so nothing needs to be copied or moved when they are passed around.
A String is returned when a function needs to do some new allocation, while &str and &mut str are slices into an existing String. Even though &mut str is mutable, you can't mutate it in a way that increases its length because that would require additional allocation.
The trim function is able to return a &str slice because that doesn't involve mutating the original string - a trimmed string is just a substring, which a slice perfectly describes. But sometimes that isn't possible; for example, a function that pads a string with an extra character would have to return a String because it would be allocating new memory.
You can reduce the number of type conversions in your code by choosing different methods:
for c in chars {
append.push(c); // append += &_char.to_string();
append = append.trim().to_string();
if append.contains("print") {
println!("print found at: \n{}", append);
append.clear(); // append = "".to_string();
}
}
There isn't anything like a trim_in_place method for String, so the way you have done it is probably the only way.

Comparing string in Rust

I want to compare a string input from stdin to a static string with no luck.
Here is what I have tried so far:
fn main() -> () {
let mut line = "".to_string();
let exit = "exit".to_string();
while line.as_slice() != exit.as_slice() {
let input = std::io::stdin().read_line().ok();
line.push_str( input.unwrap().to_string().as_slice() );
assert_eq!(line, exit);
}
}
However during assertion it failed. How should I compare a string input to a static string in Rust?
Your help is very much appreciated.
First of all, the line contains the line terminator. You probably want to use trim (or one of its variants) to ignore that.
Secondly, you're doing a lot of unnecessary conversions and allocations. Try to avoid those.
Third, to_string is (or at least, was the last time I checked) inefficient due to over-allocation. You want into_string.
Fourth, the quickest way to go from a String to a &str is to "cross-borrow" it; given a String s, &*s will re-borrow it as a &str. This is because a String implements Deref<&str>; in other words, String acts kind of like a smart pointer to a borrowed string, allowing it to decay into a simpler form.
Fifth, unless you're doing something unusual, you can rewrite this as a for loop using the lines iterator method.
Sixth, be aware that stdin() actually allocates a new buffered reader every time you call it. Not only that, but characters read into the buffer do not get "pushed back" into STDIN when a new buffer is created; that data is simply lost. So you really don't want to be calling it in a loop. If you need to, call it once and keep the result in a variable.
So, I end up with this:
fn main() {
for line in std::io::stdin().lines() {
// Extract the line, or handle the error.
let line = match line {
Ok(line) => line,
Err(err) => panic!("failed to read line: {}", err)
};
assert_eq!(line.trim(), "exit");
}
}

Resources