我写了一个函数来标题(首字母大写,所有其他小写)是一个借来的字符串,但它最终变得比它应该的更麻烦。
fn titlecase_word(word: &mut String) {
unsafe {
let buffer = word.as_mut_vec().as_mut_slice();
buffer[0] = std::char::to_uppercase(buffer[0] as char) as u8;
for i in range(1, buffer.len()) {
buffer[i] = std::char::to_lowercase(buffer[i] as char) as u8;
}
}
}
不安全的阻塞特别不合需要。有没有更好的方法来通过索引修改String内容?
答案 0 :(得分:15)
更新:针对最新的Rust进行了更新。从Rust 1.0.0-alpha开始,to_lowercase()
/ to_uppercase()
现在是CharExt
特征中的方法,并且不再有单独的Ascii
类型:ASCII操作现在被收集到两个特质,AsciiExt
和OwnedAsciiExt
。它们被标记为不稳定,因此它们可能会在整个Rust测试期间发生变化。
您的代码不正确,因为它访问单个字节以执行基于字符的操作,但UTF-8字符不是字节。对于非ASCII的任何内容,它都无法正常工作。
实际上,没有办法正确地就地执行此操作,因为任何字符转换都可能会更改字符占用的字节数,这将需要完整的字符串重新分配。您应该迭代字符并将它们收集到一个新字符串:
fn titlecase_word(word: &mut String) {
if word.is_empty() { return; }
let mut result = String::with_capacity(word.len());
{
let mut chars = word.chars();
result.push(chars.next().unwrap().to_uppercase());
for c in chars {
result.push(c.to_lowercase());
}
}
*word = result;
}
(试试here)
因为无论如何你需要生成一个新字符串,最好只返回它,而不是替换旧字符串。在这种情况下,最好将切片传递给函数:
fn titlecase_word(word: &str) -> String {
let mut result = String::with_capacity(word.len());
if !word.is_empty() {
let mut chars = word.chars();
result.push(chars.next().unwrap().to_uppercase());
for c in chars {
result.push(c.to_lowercase());
}
}
result
}
(试试here)
同样String
来自Extend
特征的extend()
方法提供了更加惯用的方法,而不是for
循环:
fn titlecase_word(word: &str) -> String {
let mut result = String::with_capacity(word.len());
if !word.is_empty() {
let mut chars = word.chars();
result.push(chars.next().unwrap().to_uppercase());
result.extend(chars.map(|c| c.to_lowercase()));
}
result
}
(试试here)
事实上,使用迭代器可以进一步缩短它:
fn titlecase_word(word: &str) -> String {
word.chars().enumerate()
.map(|(i, c)| if i == 0 { c.to_uppercase() } else { c.to_lowercase() })
.collect()
}
(试试here)
但是,如果您事先知道您正在使用ASCII,则可以使用std::ascii
模块提供的特征:
fn titlecase_word(word: String) -> String {
use std::ascii::{AsciiExt, OwnedAsciiExt};
assert!(word.is_ascii());
let mut result = word.into_bytes().into_ascii_lowercase();
result[0] = result[0].to_ascii_uppercase();
String::from_utf8(result).unwrap()
}
(试试here)
如果输入字符串包含任何非ASCII字符,则此函数将失败。
此函数不会分配任何内容,并会就地修改字符串内容。但是,如果没有不安全的和而没有额外的分配,则无法使用单个&mut String
参数编写此类函数,因为它需要从&mut
移出,这是不允许的。
你可以使用std::mem::swap()
和一个带有空字符串的临时变量 - 它不需要不安全但它可能需要分配空字符串。我不记得它是否确实需要分配;如果没有,那么你可以编写这样的函数,虽然代码会有点麻烦。无论如何,&mut
- 对于Rust来说,参数并不是真正的惯用语。