- Rustの勉強を始めて3日目ぐらいでハマりました。より良い方法を教えていただきたいので投稿します。普段はPythonです。
- 間違っているところ、Rustの理念に反しているところがたくさんあると思いますので、ご指摘よろしくお願いします。
##何をやりたいか
- yukicoderのNo.163
- 文字列中の小文字を大文字に、大文字を小文字に変換したい
- Ex) CapsLock -> cAPSlOCK
##TL;DR
皆様のコメントを受けて以下のようになりました。
(@takayahiltonさん、@tatsuya6502、@woxtuさんありがとうございました。)
fn capslock(c: char) -> char {
if c.is_uppercase() {
c.to_ascii_lowercase()
} else {
c.to_ascii_uppercase()
}
}
fn convert(string: &str) -> String {
string.chars().map(capslock).collect()
}
##はまった箇所
- 1文字ずつ変換を行わなければいけないのでとりあえずmap関数にかけたい。
- 文字列をchar型のiteratorに変換するのはchars()を使うらしい
- char型にはis_uppercase()やto_uppercase()などがあるのでこれは使えそう
- 簡単のために一文字ずつ大文字に変換するコードを素直に書くと
// エラー
fn convert(string: &str) -> String {
string.chars().map(|x| x.to_uppercase()).collect::<String>()
}
- これがなぜエラーになるかというとto_uppercase()がiteratorを返すから
- なぜiteratorを返すかというと大文字のコードポイントが複数になる場合があるかららしい(参照)
- 複数のコードポイントを返すのは
'fi' -> "FI"
や'ß' -> "SS"
などの合字が返ってくる可能性があるから(@takayahiltonさんありがとうございました。) - このiteratorをstring/&strに変更する方法がよくわからなかったので、charをstringにしてから大文字に変更する戦略へ
- mapをflat_mapに変更するとこのまま動きます。(@takayahiltonさんありがとうございました。)
##皆様のコメントを受けて
- 英語のみに対応するのであれば、char型のto_uppercase()ではなくto_ascii_uppercase()を利用するのが楽
##コード
use std::ascii::AsciiExt;
fn getline() -> String{
let mut ret = String::new();
std::io::stdin().read_line(&mut ret).ok();
ret
}
fn capslock(c: char) -> char {
if c.is_uppercase() {
c.to_ascii_lowercase()
} else {
c.to_ascii_uppercase()
}
}
fn convert(string: &str) -> String {
string.chars().map(capslock).collect()
}
fn main() {
let s = getline();
println!("{}", convert(s.trim()));
}
##感想
- 本当はchars()を使ってはいけないんだと思う(コードポイントが1つになるとは限らないので)
- しかしgrapheme関連のAPIがunstableなので断念
- C++をやりたくがないために勉強をしているが、Rustのありがたみを知るためにはC++の知識が必要そうです。
- Rubyに近い?