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.
Related
Say I have a struct Foo that owns a string:
struct Foo {
owned_string: String
}
I want to implement some methods on this struct that return substrings from the owned String. For efficiency reasons, I don't want to allocate any new memory for this, I just want the return values to point to the original String.
Let's say I know the substring I want, it's characters 10 through 15.
I can't just slice it like self.owned_string[10..16], since that would give me bytes, not characters.
I can take the characters and collect them into a new String object, like self.owned_string.chars().skip(9).take(6).collect::<String>(), but that creates a new String object. String objects own their strings (AFAIK), so presumably new memory was allocated for this, which is not what I want.
How do I create string slices that reference a substring of a String object, but using character positions? (Without allocating any new memory)
You can use char_indices() then slice the string according to the positions the iterator gives you:
let mut iter = s.char_indices();
let (start, _) = iter.nth(10).unwrap();
let (end, _) = iter.nth(5).unwrap();
let slice = &s[start..end];
However, note that as mentioned in the documentation of chars():
It’s important to remember that char represents a Unicode Scalar Value, and might not match your idea of what a ‘character’ is. Iteration over grapheme clusters may be what you actually want. This functionality is not provided by Rust’s standard library, check crates.io instead.
#ChayimFriedman's answer is of course correct, I just wanted to contribute a more telling example:
fn print_string(s: &str) {
println!("String: {}", s);
}
fn main() {
let s: String = "🤣😄😁😆😅".to_string();
let mut iter = s.char_indices();
// Retrieve the position of the char at pos 1
let (start, _) = iter.nth(1).unwrap();
// Now the next char will be at position `2`. Which would be
// equivalent of querying `.next()` or `.nth(0)`.
// So if we query for `nth(2)` we query 3 characters; meaning
// the position of character 4.
let (end, _) = iter.nth(2).unwrap();
// Gives you a &str, which is exactly what you want.
// A reference to a substring, zero allocations, zero overhead.
let substring = &s[start..end];
print_string(&s);
print_string(substring);
}
String: 🤣😄😁😆😅
String: 😄😁😆
I've done it with smileys because smileys are definitely multi-byte unicode characters.
As #ChayimFriedman already noted, the reason why we have to iterate through the char_indices is because unicode characters are variably sized. They can be anywhere from 1 to 8 bytes long, so the only way to find out where the character boundaries are is to actually read the string up to the character we desire.
I have a string slice:
fn my_func(s: &str) {
let chars: Vec<char> = s.chars().collect();
let some_char = chars[5];
//.........
}
Since a string slice is basically a pointer to a string and it's a pointer that can point to any character of a string, not necessarily the 0-th or 1st one, right?, is there a way to go back N characters and refer to the -Nth character of the original string? Not the -Nth character of a slice, but of the original string.
fn my_func(s: &str) {
let chars: Vec<char> = s.chars().collect();
//let some_char2 = chars[-10]; // minus ???
//.........
}
Provided that I know that it may cause a memory segmentation error and am fine with it.
it's a pointer that can point to any character of a String, not necessarily the 0-th or 1st one, right?
Yes.
is there a way to go back N characters and refer to -N character of an original String? Not to -Nth character of a slice, but an original String.
The slice itself retains no knowledge of whatever originally created it (which might not even be a String), it only has a pointer and a length.
Provided that I know that it may cause memory segmentation error and am fine with it.
You can always do that with unsafe and raw pointers but as the name notes this is wildly unsafe, not just because there's no guarantees whatsoever as to where's what, but because the other segments might be mutably borrowed or any other nonsense, which land you straight into UB. A segfault is the least of your worries.
What are you actually trying to achieve?
I'd like to capitalize the first letter of a &str. It's a simple problem and I hope for a simple solution. Intuition tells me to do something like this:
let mut s = "foobar";
s[0] = s[0].to_uppercase();
But &strs can't be indexed like this. The only way I've been able to do it seems overly convoluted. I convert the &str to an iterator, convert the iterator to a vector, upper case the first item in the vector, which creates an iterator, which I index into, creating an Option, which I unwrap to give me the upper-cased first letter. Then I convert the vector into an iterator, which I convert into a String, which I convert to a &str.
let s1 = "foobar";
let mut v: Vec<char> = s1.chars().collect();
v[0] = v[0].to_uppercase().nth(0).unwrap();
let s2: String = v.into_iter().collect();
let s3 = &s2;
Is there an easier way than this, and if so, what? If not, why is Rust designed this way?
Similar question
Why is it so convoluted?
Let's break it down, line-by-line
let s1 = "foobar";
We've created a literal string that is encoded in UTF-8. UTF-8 allows us to encode the 1,114,112 code points of Unicode in a manner that's pretty compact if you come from a region of the world that types in mostly characters found in ASCII, a standard created in 1963. UTF-8 is a variable length encoding, which means that a single code point might take from 1 to 4 bytes. The shorter encodings are reserved for ASCII, but many Kanji take 3 bytes in UTF-8.
let mut v: Vec<char> = s1.chars().collect();
This creates a vector of characters. A character is a 32-bit number that directly maps to a code point. If we started with ASCII-only text, we've quadrupled our memory requirements. If we had a bunch of characters from the astral plane, then maybe we haven't used that much more.
v[0] = v[0].to_uppercase().nth(0).unwrap();
This grabs the first code point and requests that it be converted to an uppercase variant. Unfortunately for those of us who grew up speaking English, there's not always a simple one-to-one mapping of a "small letter" to a "big letter". Side note: we call them upper- and lower-case because one box of letters was above the other box of letters back in the day.
This code will panic when a code point has no corresponding uppercase variant. I'm not sure if those exist, actually. It could also semantically fail when a code point has an uppercase variant that has multiple characters, such as the German ß. Note that ß may never actually be capitalized in The Real World, this is the just example I can always remember and search for. As of 2017-06-29, in fact, the official rules of German spelling have been updated so that both "ẞ" and "SS" are valid capitalizations!
let s2: String = v.into_iter().collect();
Here we convert the characters back into UTF-8 and require a new allocation to store them in, as the original variable was stored in constant memory so as to not take up memory at run time.
let s3 = &s2;
And now we take a reference to that String.
It's a simple problem
Unfortunately, this is not true. Perhaps we should endeavor to convert the world to Esperanto?
I presume char::to_uppercase already properly handles Unicode.
Yes, I certainly hope so. Unfortunately, Unicode isn't enough in all cases.
Thanks to huon for pointing out the Turkish I, where both the upper (İ) and lower case (i) versions have a dot. That is, there is no one proper capitalization of the letter i; it depends on the locale of the the source text as well.
why the need for all data type conversions?
Because the data types you are working with are important when you are worried about correctness and performance. A char is 32-bits and a string is UTF-8 encoded. They are different things.
indexing could return a multi-byte, Unicode character
There may be some mismatched terminology here. A char is a multi-byte Unicode character.
Slicing a string is possible if you go byte-by-byte, but the standard library will panic if you are not on a character boundary.
One of the reasons that indexing a string to get a character was never implemented is because so many people misuse strings as arrays of ASCII characters. Indexing a string to set a character could never be efficient - you'd have to be able to replace 1-4 bytes with a value that is also 1-4 bytes, causing the rest of the string to bounce around quite a lot.
to_uppercase could return an upper case character
As mentioned above, ß is a single character that, when capitalized, becomes two characters.
Solutions
See also trentcl's answer which only uppercases ASCII characters.
Original
If I had to write the code, it'd look like:
fn some_kind_of_uppercase_first_letter(s: &str) -> String {
let mut c = s.chars();
match c.next() {
None => String::new(),
Some(f) => f.to_uppercase().chain(c).collect(),
}
}
fn main() {
println!("{}", some_kind_of_uppercase_first_letter("joe"));
println!("{}", some_kind_of_uppercase_first_letter("jill"));
println!("{}", some_kind_of_uppercase_first_letter("von Hagen"));
println!("{}", some_kind_of_uppercase_first_letter("ß"));
}
But I'd probably search for uppercase or unicode on crates.io and let someone smarter than me handle it.
Improved
Speaking of "someone smarter than me", Veedrac points out that it's probably more efficient to convert the iterator back into a slice after the first capital codepoints are accessed. This allows for a memcpy of the rest of the bytes.
fn some_kind_of_uppercase_first_letter(s: &str) -> String {
let mut c = s.chars();
match c.next() {
None => String::new(),
Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
}
}
Is there an easier way than this, and if so, what? If not, why is Rust designed this way?
Well, yes and no. Your code is, as the other answer pointed out, not correct, and will panic if you give it something like བོད་སྐད་ལ་. So doing this with Rust's standard library is even harder than you initially thought.
However, Rust is designed to encourage code reuse and make bringing in libraries easy. So the idiomatic way to capitalize a string is actually quite palatable:
extern crate inflector;
use inflector::Inflector;
let capitalized = "some string".to_title_case();
It's not especially convoluted if you are able to limit your input to ASCII-only strings.
Since Rust 1.23, str has a make_ascii_uppercase method (in older Rust versions, it was available through the AsciiExt trait). This means you can uppercase ASCII-only string slices with relative ease:
fn make_ascii_titlecase(s: &mut str) {
if let Some(r) = s.get_mut(0..1) {
r.make_ascii_uppercase();
}
}
This will turn "taylor" into "Taylor", but it won't turn "édouard" into "Édouard". (playground)
Use with caution.
I did it this way:
fn str_cap(s: &str) -> String {
format!("{}{}", (&s[..1].to_string()).to_uppercase(), &s[1..])
}
If it is not an ASCII string:
fn str_cap(s: &str) -> String {
format!("{}{}", s.chars().next().unwrap().to_uppercase(),
s.chars().skip(1).collect::<String>())
}
The OP's approach taken further:
replace the first character with its uppercase representation
let mut s = "foobar".to_string();
let r = s.remove(0).to_uppercase().to_string() + &s;
or
let r = format!("{}{s}", s.remove(0).to_uppercase());
println!("{r}");
works with Unicode characters as well eg. "😎foobar"
The first guaranteed to be an ASCII character, can changed to a capital letter in place:
let mut s = "foobar".to_string();
if !s.is_empty() {
s[0..1].make_ascii_uppercase(); // Foobar
}
Panics with a non ASCII character in first position!
Since the method to_uppercase() returns a new string, you should be able to just add the remainder of the string like so.
this was tested in rust version 1.57+ but is likely to work in any version that supports slice.
fn uppercase_first_letter(s: &str) -> String {
s[0..1].to_uppercase() + &s[1..]
}
Here's a version that is a bit slower than #Shepmaster's improved version, but also more idiomatic:
fn capitalize_first(s: &str) -> String {
let mut chars = s.chars();
chars
.next()
.map(|first_letter| first_letter.to_uppercase())
.into_iter()
.flatten()
.chain(chars)
.collect()
}
This is how I solved this problem, notice I had to check if self is not ascii before transforming to uppercase.
trait TitleCase {
fn title(&self) -> String;
}
impl TitleCase for &str {
fn title(&self) -> String {
if !self.is_ascii() || self.is_empty() {
return String::from(*self);
}
let (head, tail) = self.split_at(1);
head.to_uppercase() + tail
}
}
pub fn main() {
println!("{}", "bruno".title());
println!("{}", "b".title());
println!("{}", "🦀".title());
println!("{}", "ß".title());
println!("{}", "".title());
println!("{}", "བོད་སྐད་ལ".title());
}
Output
Bruno
B
🦀
ß
བོད་སྐད་ལ
Inspired by get_mut examples I code something like this:
fn make_capital(in_str : &str) -> String {
let mut v = String::from(in_str);
v.get_mut(0..1).map(|s| { s.make_ascii_uppercase(); &*s });
v
}
Currently I'm using the following function to extract prefix substrings:
fn prefix(s: &String, k: usize) -> String {
s.chars().take(k).collect::<String>()
}
This can then be used for comparisons like so:
let my_string = "ACGT".to_string();
let same = prefix(&my_string, 3) == prefix(&my_string, 2);
However, this allocates a new String for each call to prefix, in addition to the processing for the iteration. Most other languages I'm familiar with have an efficient way to do a comparison like this, using just a view of the strings. Is there a way in Rust?
Yes, you can take subslices of strings using the Index operation:
fn prefix(s: &str, k: usize) -> &str {
&s[..k]
}
fn main() {
let my_string = "ACGT".to_string();
let same = prefix(&my_string, 3) == prefix(&my_string, 2);
println!("{}", same);
}
Note that slicing a string uses bytes as the unit, not characters. It is up to the programmer to ensure that the slice lengths lie on valid UTF-8 boundaries. Additionally, you have to ensure that you don't try to slice past the end of the string. Breaking either of these will result in a panic!.
A bit more defensive version would be
fn prefix(s: &str, k: usize) -> &str {
let idx = s.char_indices().nth(k).map(|(idx, _)| idx).unwrap_or(s.len());
&s[0..idx]
}
The key difference is that we use the char_indices iterator, which tells us the byte offsets corresponding to a character. Indexing into a UTF-8 string is an O(n) operation, and Rust doesn't want to hide that algorithmic complexity from you. This still isn't even complete, because there can be combining characters, for example. Dealing with strings is hard, thanks to the complexity of human language.
Most other languages I'm familiar with have an efficient way
Doubtful :-) To be efficient in time, they'd have to know how many bytes to skip ahead for every character. Either they'd have to keep a lookup table for every string or use a fixed-size character encoding. Both of those solutions can use more memory than needed, and a fixed size encoding doesn't even work when you have combining characters, for example.
Of course, other languages could just say "LOL, strings are just arrays of bytes, good luck with treating them correctly", and efficiently ignore your character encoding...
Two additional notes
Your predicate doesn't really make sense. A string of 2 letters will never match one of 3 letters. For strings to match, they must have the same amount of bytes.
You should never need to take &String as a function argument. Taking a &str is a more accepting argument in all cases except for one teeny tiny little case that no one needs — knowing the capacity of a String, but without being able to modify the string.
While Shepmaster's answer is absolutely correct for the general case of string slicing, I'd like to add that sometimes there are easier ways.
If you know in advance the set of characters you're working with ("ATGC" example suggests you're working with nucleobases, so it is possible that these are all the characters you need) then you can use slices of bytes &[u8] instead of string slices &str. You can always get a byte slice out of a string slice and a Vec<u8> out of a String, if necessary:
let s: String = "ATGC".into();
let ss: &str = &s;
let b: Vec<u8> = s.into_bytes();
let bs: &[u8] = ss.as_slice();
Also, there are byte slice and byte character literals, just prefix regular string/char literals with b:
let sl: &[u8] = b"ATGC";
let bl: u8 = b'G';
Working with byte slices give you constant-time indexing (and thus slicing) operations, so checking for prefix equality is easy (like Shepmaster's first variant but without possibility of panics (unless k is too large):
fn prefix(s: &[u8], k: usize) -> &[u8] {
&s[..k]
}
If you need, you can turn byte slices/vectors back to strings. This operation, of course, checks validity of UTF-8 encoding so it may fail, but if you only work with ASCII, you can safely ignore these errors and just unwrap():
let ss2: &str = str::from_utf8(bs).unwrap();
let s2: String = String::from_utf8(b).unwrap();
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.