Ответ 1
Пусть сломается, по очереди
let s1 = "foobar";
Мы создали литеральную строку, которая закодирована в UTF-8. UTF-8 позволяет нам кодировать 1,114,112 кодовых точек Unicode таким образом, что это довольно компактно, если вы попали из региона мира, который в основном использует символы, найденные в ASCII, стандарте, созданном в 1963 году. UTF-8 - это переменная длина кодирование, что означает, что одна точка кода может занимать от 1 до 4 байтов. Более короткие кодировки зарезервированы для ASCII, но многие кандзи занимают 3 байта в UTF-8.
let mut v: Vec<char> = s1.chars().collect();
Это создает вектор char
. Символ представляет собой 32-битное число, которое непосредственно сопоставляется с кодовой точкой. Если бы мы начали с текста, содержащего только ASCII, мы увеличили в четыре раза наши требования к памяти. Если бы у нас была куча персонажей из астрального плана, то, возможно, мы не использовали это намного больше.
v[0] = v[0].to_uppercase().nth(0).unwrap();
Это захватывает первую кодовую точку и запрашивает ее преобразование в верхний регистр. К сожалению, для тех из нас, кто вырос по-английски, не всегда есть однократное сопоставление "маленькой буквы" с "большой буквой". Боковое замечание: мы называем их upper- и нижним регистром, потому что одна буква букв была над другим полем писем в тот же день.
Этот код будет паниковать, если точка кода не имеет соответствующего варианта в верхнем регистре. Я не уверен, существуют ли на самом деле. Он также может семантически терпеть неудачу, если точка кода имеет вариант с верхним регистром, который имеет несколько символов, например, немецкий ß
. Обратите внимание, что ß никогда не может быть капитализирована в реальном мире, это единственный пример, который я всегда могу запомнить и найти. По состоянию на 2017-06-29, на самом деле, официальные правила немецкой орфографии были обновлены так, что как "ẞ" и "СС" являются действительной капитализацией !
let s2: String = v.into_iter().collect();
Здесь мы преобразуем символы обратно в UTF-8 и требуем нового размещения для их хранения, поскольку исходная переменная хранилась в постоянной памяти, чтобы не занимать память во время выполнения.
let s3 = &s2;
И теперь мы ссылаемся на эту String
.
Это простая проблема
К сожалению, это не так. Может быть, нам стоит попытаться превратить мир в эсперанто?
Я предполагаю, что
char::to_uppercase
уже правильно обрабатывает Unicode.
Да, я, конечно, надеюсь. К сожалению, Unicode недостаточно во всех случаях. Благодаря huon для указания турецкого I, где и верхняя (İ), и нижняя (i) версии имеют точку. То есть, нет никакой надлежащей капитализации буквы i
; это зависит от языка исходного текста.
зачем нужна конвертация всех типов данных?
Поскольку типы данных, с которыми вы работаете, важны, когда вас беспокоят правильность и производительность. char
32-бит, а строка кодируется UTF-8. Это разные вещи.
индексирование может возвращать многобайтовый символ Unicode
Здесь может быть некоторая несовпадающая терминология. char
является многобайтовым символом Юникода.
Нарезка строки возможна, если вы побиваете байты, но стандартная библиотека будет паниковать, если вы не находитесь на границе символа.
Одна из причин, по которой индексирование строки для получения символа никогда не выполнялась, заключается в том, что так много людей злоупотребляют строками как массивы символов ASCII. Индексирование строки для установки символа никогда не могло быть эффективным - вы должны были бы иметь возможность заменить 1-4 байта со значением, равным 1-4 байтам, в результате чего остальная часть строки будет сильно отскакивать.
to_uppercase
может возвращать символ верхнего регистра
Как упоминалось выше, ß
является единственным символом, который при заглавной записи становится двумя символами.
Если бы мне пришлось написать код, это выглядело бы так:
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("ß"));
}
Но я бы, вероятно, искал прописные или unicode на crates.io, и пусть кто-то умнее меня справится с этим.
Говоря о "чем-то умнее меня", Veedrac указывает, что, вероятно, более эффективно преобразовать итератор обратно в срез после того, как будут доступны первые ключевые кодовые точки. Это позволяет memcpy
остальных байтов.
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(),
}
}