I am working on matching commands from my Redis wannabe. I have the following:
let line = self.parse_stream(&buffer, len);
println!("{:?}", line);
match line {
line => {
match line[2] {
"ECHO" => {
self.write_frame(
&Frame::Bulk(line[4].to_string())).await?;
}
"echo" => {
...
I am having to duplicate match code because I can't figure out how to change "ECHO" to lower case. On the other hand, am I saving any stack memory by keeping the string slice or should I just convert all to String or &String?
If you want to handle mixed case, that will require 16 literals or performing a case conversion. If it's okay to mutate buffer, then you can use str::make_ascii_uppercase to do that in-place without further allocation.
l if l.eq_ignore_ascii_case("echo") => {
self.write_frame(
&Frame::Bulk(line[4].to_string())).await?;
}
Of course, since likely multiple strings are being matched against, one can avoid the need to do re-do the case-conversion upon each comparison, and also make the match more readable, by using make_ascii_lowercase or …uppercase nonetheless, and matching against "echo" or "ECHO" in the match, respectively.
The or pattern is interesting. I thought I tried that and got an error. Been working too hard. I think that's the best way to go. Trying str::make_ascii_uppercase created problems since I parsed a buffer and put the results in a Vec<&String>. Then other parts of the code would error with Got String expected &str.
Here's where I parse the buffer. It's not original but lifted from the fine folks at Tokio and probably altered. If I try to use make_ascii_uppercase here, I get an error that I can't borrow the local variable &line[start..i] as mutable. Don't know if that has something to do with the lifetime.
fn parse_stream<'a>(&'a self, line: &'a [u8], l: usize) -> Vec<&str> {
let mut lines = vec![];
let mut start = 0;
let end = l - 1;
for i in start..end {
if line[i] == b'\r' && line[i + 1] == b'\n' {
lines.push(std::str::from_utf8(&line[start..i]).unwrap());
start = i + 2;
}
}
return lines
}
Ok, fixed that. I assume that self doesn't need the lifetime because self does hang around for the life of the program and removing that requires the life time is put on the return type to make sure it lasts after the return. But where do I use str::make_ascii_uppercase?
For that, you'd change the line type to &mut [u8] and apply it right before str::from_utf8(). That'd require splitting the borrows. An easier solution might be to just apply it before you do anything else with the string.
The do-it-upfront approach is trivial; the split-the-borrow approach is demonstrated here.
I was answering just before I saw you edited your answer. Yep, still having issues.
lines.push(str::make_ascii_lowercase(std::str::from_utf8(&line[start..i]).unwrap()));
| ------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability
| |
| arguments to this function are incorrect
|
Thanks. I'll look the demo
Actually, the easiest appears to be to go with what I have in the match portion so that this
match line {
line => {
match line[2] {
"ECHO" | "echo" => {
self.write_frame(
&Frame::Bulk(line[4].to_string())).await?;
},
"PING" | "ping" => {
self.write_frame(
&Frame::Simple("PONG".to_string())).await?;
}
Doing otherwise the only thing I can make work is to return Vec from stream_parse, then I convert my match terms as "echo".to_string() but I am under the impression that String takes up more memory and I could be very wrong. Thank you for the all the help. I forgot to ask. Was I correct that a lifetime on self is just silly. Inside the Impl the lifetime the length for the program unless explicitly "dropped"?
Don't use str::make_ascii_lowercase. I intentionally used the slice version, not the str version. The str version requires a &mut str which is really hard to get hold of.