A more convenient concatenation with a string literal in Rust - rust

In the nightly Rust it is no longer possible to designate a string literal as String with a "~" character.
In C++, for example, I'm using user-defined literals to concatenate string literals without the crust of mentioning std::string every time:
inline std::string operator"" _s (const char* str, size_t size) {return std::string (str, size);}
foo ("Hello, "_s + "world!");
Is there a similar feature existing or planned in Rust to make string literal concatenation less painful than String::from_str ("Hello, ") + "world!"?

If you literally (hah) have string literals, you can use the concat! macro:
let lit = concat!("Hello, ", "world!")
You can natively split strings over several lines:
let lit = "Hello, \
World";
The \ consumes all following whitespace, including the leading spaces on the next line; omitting the \ will include the string data "verbatim", with newlines and leading spaces etc.
You can add a &str to a String:
let s = "foo".to_string() + "bar" + "baz";
You could use push_str iteratively:
let mut s = "foo".to_string();
s.push_str("bar");
s.push_str("baz");
You could use SliceConcatExt::concat:
let s = ["foo", "bar", "baz"].concat();
If all else fails, you can define a macro to do exactly what you want.
See also:
How to concatenate static strings in Rust
What is the syntax for a multiline string literal?
How do I concatenate strings?

You can use the format! macro. It is more readable, more translation-friendly, more efficient, and more powerful (you can concatenate more than just strings, just like C++'s ostringstream). It is also completely type-safe.
format!("Hello, {}", "world!")
You can also use named arguments to improve readability.
format!("hello, {who}", who = "world")
The full formatting syntax is described in std::fmt.
Rust does not have user-defined literals. I think adding such a feature is backward-compatible, so maybe this feature will be added after Rust 1.0.

You can concatenate str literals without using String with the concat! macro:
let input = concat!("Hello", ' ', "world");
To make it a string, specify the destination type and use into:
let input: String = concat!("Hello", ' ', "world").into();
Full program:
fn main() {
let input: String = concat!("Hello", ' ', "world").into();
println!("{}", input); // Output: Hello world
}

Related

Palindrome code in rust programming language

I am trying to write a palindrome program in Rust.
Even when the input is a palindrome word, my attempt is not showing a palindrome:
use std::io;
fn main(){
println!("enter a word to know if palindrome or not");
let mut inp=String::new();
io::stdin().read_line(&mut inp).expect("needed a string");
let arr:Vec<_>=inp.chars().collect();
let mut new_st=String::new();
for i in 0..arr.len(){
new_st.push(arr[arr.len()-1-i]);
}
if inp.eq(&new_st[1..]) {
println!("Palindrome");
}
else{
println!("not a palindrome..");
}
println!("{}",&new_st[1..]);
}
Output:
enter a word to know if palindrome or not
amma
not a palindrome..
amma
The problem is that the .read_line() function adds a \n to the end of inp string. You should remove it from the string or better yet use the .trim() method on the String to strip out any newline or whitespace characters.
inp = inp.trim().to_string();
Just some more improvement on your code, you should leverage Rust's iterators to reverse the String faster rather than manually doing it.
You can write this:-
let rev = inp.chars().rev().collect::<String>();
This iterates over all the characters in the inp string and reverses them in order. Finally it collects them into a String that is stored in the rev variable.
Also, you should use == rather than using the .eq() operator, it's just much more clearer.
See this playground link for complete code

trying to trim and lowercase my String in rust

I'm trying to trim and lowercase my String.
Currently I have
use dialoguer::Input;
let input: String = Input::new()
.with_prompt("Guess a 5 letter word")
.interact_text()
.unwrap();
let guess: &str = input.as_str(); // trim and lowercase
I'm trying to transform String into a trimmed and lowercased &str but some functions are only on &str and others only on String so I'm having trouble coming up with an elegant solution.
TL;DR: End goal have guess be a trimmed and lowercase &str
Rust stdlib is not about elegance, but about correctness and efficiency.
In your particular case, trim() is defined as str::trim(&self) -> &str because it always returns a substring of the original string, so it does not need to copy or allocate a new string, just compute the begin and end, and do the slice.
But to_lowercase() is defined as str::to_lowercase(&self) -> String because it changes each of its characters to the lowercase equivalent, so it must allocate and fill a new String.
You may thing that if you own the string you can mutate it to lowercase in-place. But that will not work in general because there is not a 1-to-1 map between lowercase and uppercase letters. Think of, for example ß <-> SS in German.
Naturally, you may know that your string only has ASCII characters... if so you can also use str::make_ascii_lowercase(&mut self) that does the change in-place, but only for ASCII characters that do have the 1-to-1 map.
So, summing up, the more ergonomic code would be, to trim input and copy to an owned lowercase:
let guess : String = input.trim().to_lowercase();
Or if you absolutely want to avoid allocating an extra string, but you are positive that only ASCII characters matter:
let mut input = input; //you could also add the mut above
input.make_ascii_lowercase();
let guess: &str = input.trim();
Try this:
let s = " aBcD ";
let s2 = s.trim().to_lowercase();
println!("[{s}], [{s2}]");
The above will work if s is &str (as in my example) or String and it will print:
[ aBcD ], [abcd]
So the last line in your code (if you insist on having guess as &str) should become:
let guess: &str = &input.trim().to_lowercase();
Otherwise if you write just:
let guess = input.trim().to_lowercase();
, guess will be of type String, as that's what to_lowercase() returns.

Parse a string containing a Unicode number into the corresponding Unicode character?

Is there a function to do something like this:
fn string_to_unicode_char(s: &str) -> Option<char> {
// ...
}
fn main() {
let s = r"\u{00AA}"; // note the raw string literal!
string_to_unicode_char(s).unwrap();
}
Note that r"\u{00AA}" uses a raw string i. e. it isn't a Unicode sequence but 8 separate symbols, as \ u { 0 0 A A }.
I need to interpret/convert/parse this string and return a char if all is good, None otherwise. I don't have any experience with Unicode, so any ideas are welcome.
I believe the function you are looking for is char::from_u32:
fn string_to_unicode_char(s: &str) -> Option<char> {
// Do something more appropriate to find the actual number
let number = &s[3..7];
u32::from_str_radix(number, 16)
.ok()
.and_then(std::char::from_u32)
}
fn main() {
let s = r"\u{00AA}"; // note the raw string literal!
let ch = string_to_unicode_char(s);
assert_eq!(ch, Some('\u{00AA}'));
}
I indeed completely misunderstood your question; my old answer can be seen in the edit logs
Is there a builtin function to parse a string containing a Rust unicode escape into the corresponding unicode character?
AFAIK, no, there is not a builtin function to do that.
The answer to "how to do it yourself" is a bit broad, as there are many ways to do it (and it's not clear whether you also want to parse standard escapes, such as "\n").
Use a regex
Do simple, naive manual parsing
Embed it into a bigger lexer (the function in the Rust compiler parsing such unicode escapes)

What is the "standard" way to concatenate strings?

While I understand basically what str and std::string::String are and how they relate to each other, I find it a bit cumbersome to compose strings out of various parts without spending too much time and thought on it. So as usual I suspect I did not see the proper way to do it yet, which makes it intuitive and a breeze.
let mut s = std::string::String::with_capacity(200);
let precTimeToJSON = | pt : prectime::PrecTime, isLast : bool | {
s.push_str(
"{ \"sec\": "
+ &(pt.sec.to_string())
+ " \"usec\": "
+ &(pt.usec.to_string())
+ if isLast {"}"} else {"},"})
};
The code above is honored by the compiler with error messages like:
src\main.rs:25:20: 25:33 error: binary operation + cannot be applied to type &'static str [E0369]
And even after half an hours worth of fiddling and randomly adding &, I could not make this compilable. So, here my questions:
What do I have to write to achieve the obvious?
What is the "standard" way to do this in Rust?
The Rust compiler is right (of course): there's no + operator for string literals.
I believe the format!() macro is the idiomatic way to do what you're trying to do. It uses the std::fmt syntax, which essentially consists of a formatting string and the arguments to format (a la C's printf). For your example, it would look something like this:
let mut s: String = String::new();
let precTimeToJSON = | pt : prectime::PrecTime, isLast : bool | {
s = format!("{{ \"sec\": {} \"usec\": {} }}{}",
pt.sec,
pt.usec,
if isLast { "" } else { "," }
)
};
Because it's a macro, you can intermix types in the argument list freely, so long as the type implements the std::fmt::Display trait (which is true for all built-in types). Also, you must escape literal { and } as {{ and }}, respectively. Last, note that the format string must be a string literal, because the macro parses it and the expanded code looks nothing like the original format! expression.
Here's a playground link to the above example.
Two more points for you. First, if you're reading and writing JSON, have a look at a library such as serde. It's much less painful!
Second, if you just want to concatenate &'static str strings (that is, string literals), you can do that with zero run-time cost with the concat!() macro. It won't help you in your case above, but it might with other similar ones.
Itertools::format can help you write this as a single expression if you really want to.
let times: Vec<PrecTime>; // iterable of PrecTime
let s = format!("{}", times.iter().format(",", |pt, f|
f(&format_args!(r#"{{ "sec": {}, "usec": {} }}"#, pt.sec, pt.usec))
));
format() uses a separator, so just specify "," there (or "" if you need no separator). It's a bit involved so that the formatting can be completely lazy and composable. You receive a callback f that you call back with a &Display value (anything that can be Display formatted).
Here we demonstrate this great trick of using &format_args!() to construct a displayable value. This is something that comes in handy if you use the debug builder API as well.
Finally, use a raw string so that we don't need to escape the inner " in the format: r#"{{ "sec": {} "usec": {} }}"#. Raw strings are delimited by r#" and "# (free choice of number of #).
Itertools::format() uses no intermediate allocations, it is all directly passed on to the underlying formatter object.
You can also do this madness:
fn main() {
let mut s = std::string::String::with_capacity(200);
// Have to put this in a block so precTimeToJSON is dropped, see https://doc.rust-lang.org/book/closures.html
{
// I have no idea why this has to be mut...
let mut precTimeToJSON = |sec: u64, usec: u64, isLast: bool| {
s.push_str(&( // Coerce String to str. See https://doc.rust-lang.org/book/deref-coercions.html
"{ \"sec\": ".to_string() // String
+ &sec.to_string() // + &str (& coerces a String to a &str).
+ " \"usec\": " // + &str
+ &usec.to_string() // + &str
+ if isLast {"}"} else {"},"} // + &str
));
};
precTimeToJSON(30, 20, false);
}
println!("{}", &s);
}
Basically the operator String + &str -> String is defined, so you can do String + &str + &str + &str + &str. That gives you a String which you have to coerce back to a &str using &. I think this way is probably quite inefficient though as it will (possibly) allocate loads of Strings.

How to partition a string at a fixed index?

I have a String (in particular a SHA1 hex digest) that I would like to split into two substrings - the first two characters and the rest of of the string. Is there a clean way to do this in Rust?
If you know that your string only contains ASCII characters (as in case with sha digests), you can use slices directly:
let s = "13e3f28a65a42bf6258cbd1d883d1ce3dac8f085";
let first = &s[..2]; // "13"
let rest = &s[2..]; // "e3f28a65a42bf6258cbd1d883d1ce3dac8f085"
It won't work correctly if your string contains non-ASCII characters because slicing uses byte offsets, and if any index used in slicing points into the middle of a code point representation, your program will panic.
There's a split_at method since Rust 1.4, use it like this:
let s = "13e3f28a65a42bf6258cbd1d883d1ce3dac8f085";
let (first, last) = s.split_at(2);
assert_eq!("13", first);
assert_eq!("e3f28a65a42bf6258cbd1d883d1ce3dac8f085", last);
Note that the index is a byte position and must lie on a character boundary. In this case this works because you know that your input string is ASCII.
If you are expecting two Strings instead of slices, you can use the chars() method and some Iterator methods to obtain them.
let text = "abcdefg".to_string();
let start: String = text.chars().take(2).collect();
let end: String = text.chars().skip(2).collect();
If you don't want to do heap allocations, you can use slices instead:
let start: &str = text.slice_chars(0, 2);
let end: &str = text.slice_chars(2, text.char_len());
Note that the slices version requires you to use unstable rust (nightly builds, not the beta)
Here is a way to efficiently split a String into two Strings, in case you have this owned string data case. The allocation of the input string is retained in the first piece by just using truncation.
/// Split a **String** at a particular index
///
/// **Panic** if **byte_index** is not a character boundary
fn split_string(mut s: String, byte_index: usize) -> (String, String)
{
let tail = s[byte_index..].into();
s.truncate(byte_index);
(s, tail)
}
Note: The .into() method is from the generic conversion trait Into and in this case it converts &str into String.

Resources