Convert from Option<String> to non-empty &str w/o move - rust

Is there a more efficient way to do this:
let s_opt = Some("Abc".to_string());
let s2 = &s_opt.clone().unwrap_or("".to_string());
println!("s_opt = {s_opt:?}, s2 = {s2:?}");
If I omit the clone() call, there will be a problem, because unwrap_or moves the wrapped String value.

I think I found what I was looking for:
let s2 = s_opt.as_deref().unwrap_or("");
It's a bit shorter and avoids cloning the original String value.

Related

Parse integer from from iterator

Is there a way to parse an integer from a str in rust, like my_str.parse(), but yielding an iterator to the point after the parsed integer? Something like this:
let my_str = "1234x";
let mut iter = my_str.chars();
assert_eq!(iter.parse_and_advance().unwrap(), 1234);
assert_eq!(iter.next(), Some('x'));
You don't need iterators at all. You can first use str::find to find the first non-numeric value, then use str::split_at to split the string there so you can parse the first half and convert the second half into an iterator:
let str = "1234x";
let non_letter = str.find(|c: char| c != '-' && !c.is_numeric());
let (num, rest) = str.split_at(non_letter.unwrap_or(str.len()));
let num: i32 = num.parse().unwrap();
let mut rest = rest.chars();
assert_eq!(num, 1234);
assert_eq!(rest.next(), Some('x'));
Playground link
Note that, as stated in the comments, there's a little more nuance than this to extracting the initial number, but depending on your usecase it won't be an issue.

Why use immutable Vector or String in Rust

I'm learning Rust and learned, that for making an expandable string or array, the String and Vec structs are used. And to modify a String or Vec, the corresponding variable needs to be annotated with mut.
let mut myString = String::from("Hello");
let mut myArray = vec![1, 2, 3];
My question is, why would you use or declare an immutable String or Vec like
let myString = String::from("Hello");
let myArray = vec![1, 2, 3];
instead of a true array or str literal like
let myString = "Hello";
let myArray = [1, 2, 3];
What would be the point of that, does it have any benefits? And what may be other use cases for immutable String's and Vec's?
Edit:
Either I am completely missing something obvious or my question isn't fully understood. I get why you want to use a mutable String or Vec over the str literal or an array, since the latter are immutable. But why would one use an immutable String or Vec over the str literal or the array (which are also immutable)?
You might then do something with that string, for example use it to populate a struct:
struct MyStruct {
field: String,
}
...
let s = String::from("hello");
let mut mystruct = MyStruct { field: s };
You could also, for example, return it from a function.
One use case could be the following:
fn main() {
let text: [u8; 6] = [0x48, 0x45, 0x4c, 0x4c, 0x4f, 0x00];
let converted = unsafe { CStr::from_ptr(text.as_ptr() as *const c_char) }.to_string_lossy().to_string();
println!("{}", converted);
}
This is a bit constructed, but imagine you want to convert a null terminated C string (which might come from the network) from a raw pointer to some Rust string, but you don't know whether it has invalid UTF-8 in it. to_string_lossy() returns a Cow which either points to the original bytes (in case everything is valid UTF-8), but if that's not the case, it will basically copy the string and do a new allocation, replace the invalid characters with the UTF-8 replacement character and then point to that.
This is of course quite nice, because (presumably) most of the time you get away without copying the original C string, but in some cases, it might not be. But if you don't care about that and don't want to work with a Cow, it might make sense to convert it to a String, which you don't need to be mutable afterwards. But it's not possible to have a &str in case the original text contains invalid UTF-8.

Rust repeat immutable variable

I am new in Rust but I know a little bit a Python, and I have questions.
Why does the code below work without any exceptions:
fn main() {
let s1 = String::from("hello");
let repeated_str = s1.repeat(4);
println!("{}",repeated_str);
}
s1 variable is not mutable. But we mutate s1 using the repeat() method right? So why we can create a new variable using s1?
Maybe I didn't understand something when I read about ownership and reference.
The str::repeat function does not actually mutate the given value, it only copies it. Hence, you are able to call the function without s1 having the mut modifier.
This is explained in the docs for repeat with the following line:
Creates a new String by repeating a string n times.

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.

String append, cannot move out of dereference of '&'pointer

I'm having trouble combining two strings, I'm very new to rust so If there is an easier way to do this please feel free to show me.
My function loops through a vector of string tuples (String,String), what I want to do is be able to combine these two strings elements into one string. Here's what I have:
for tup in bmp.bitmap_picture.mut_iter() {
let &(ref x, ref y) = tup;
let res_string = x;
res_string.append(y.as_slice());
}
but I receive the error : error: cannot move out of dereference of '&'-pointer for the line: res_string.append(y.as_slice());
I also tried res_string.append(y.clone().as_slice()); but the exact same error happened, so I'm not sure if that was even right to do.
The function definition of append is:
fn append(self, second: &str) -> String
The plain self indicates by-value semantics. By-value moves the receiver into the method, unless the receiver implements Copy (which String does not). So you have to clone the x rather than the y.
If you want to move out of a vector, you have to use move_iter.
There are a few other improvements possible as well:
let string_pairs = vec![("Foo".to_string(),"Bar".to_string())];
// Option 1: leave original vector intact
let mut strings = Vec::new();
for &(ref x, ref y) in string_pairs.iter() {
let string = x.clone().append(y.as_slice());
strings.push(string);
}
// Option 2: consume original vector
let strings: Vec<String> = string_pairs.move_iter()
.map(|(x, y)| x.append(y.as_slice()))
.collect();
It seems like you might be confusing append, which takes the receiver by value and returns itself, with push_str, which simply mutates the receiver (passed by mutable reference) as you seem to expect. So the simplest fix to your example is to change append to push_str. You'll also need to change "ref x" to "ref mut x" so it can be mutated.

Resources