🦀
Ruby脳のためのRust文字列系メソッドまとめ
Ruby | Rust |
---|---|
bytesize | len |
[] | get |
chars | chars |
size | chars.count |
to_i | parse::<isize>() |
lines(chomp: true) | lines |
strip | trim |
lstrip | trim_start |
rstrip | trim_end |
split | split_whitespace |
split(x) | split(x) |
gsub!(str, "") | remove_matches(str) |
concat(str) | push_str(str) |
concat(ch) | push(ch) |
self * n | repeat(n) |
clear | clear |
empty? | is_empty |
include? | contains |
chars.each.with_index | char_indices |
inspect | escape_debug |
gsub | replace |
gsub 最初のn回だけ | replacen |
insert(i, str) | insert_str(i, str) |
insert(i, ch) | insert(i, ch) |
new | new |
bytes | into_bytes |
bytes | as_bytes |
bytes | as_mut_vec |
slice!(n..) | truncate(n) |
slice!(-1) | pop |
slice!(i) | remove(i) |
select! 類似 | retain |
slice!(i..) | split_off(i) |
[range] = v | replace_range(range, v) |
slice | get_mut |
split(x).reverse | rsplit(x) |
split(/(?<=x)/) | split_inclusive(x) |
split(x) | split_terminator(x) |
split(x).reverse | rsplit_terminator(x) |
split(sep, n) | splitn(n, sep) |
split 末尾から | rsplitn(n, sep) |
split(sep, 2) | split_once(sep) |
split(sep, 2) 末尾から | rsplit_once(sep) |
scan | matches |
reverse.scan | rmatches |
start_with? | starts_with |
end_with? | ends_with |
ascii_only? | is_ascii |
index | find |
rindex | rfind |
strip 類似 | trim_matches |
lstrip 類似 | trim_start_matches |
rstrip 類似 | trim_end_matches |
delete_prefix | strip_prefix |
delete_suffix | strip_suffix |
casecmp?(other) | eq_ignore_ascii_case |
upcase | to_uppercase |
downcase | to_lowercase |
tr("a-z", "A-Z") | to_ascii_uppercase |
tr("A-Z", "a-z") | to_ascii_lowercase |
tr!("a-z", "A-Z") | make_ascii_uppercase |
tr!("A-Z", "a-z") | make_ascii_lowercase |
? | match_indices |
? | rmatch_indices |
dump 一部unicode | escape_default |
dump 全部unicode | escape_unicode |
to_s | to_string |
to_s | as_str |
to_s 更新用 | as_mut_str |
ary.pack("C*") | from_utf8(ary) |
ary.pack("C*").scrub | from_utf8_lossy(ary) |
new(capacity: xxx) | with_capacity(xxx) |
? | reserve(size) |
? | shrink_to_fit |
? | into_raw_parts |
? | from_raw_parts |
? | into_boxed_str |
? | is_char_boundary |
? | as_ptr |
? | as_mut_ptr |
bytesize
→ len
Ruby
"🥑".bytesize # => 4
Rust
"🥑".len() // => 4
Ruby の配列は文字単位になっているのに対して Rust はバイト単位の配列になっている
[]
→ get
Ruby
s = "A🥑B"
s[0] # => "A"
s[1] # => "🥑"
s[2] # => "B"
Rust
let s = "A🥑B";
s.get(0..1) // => Some("A")
s.get(1..5) // => Some("🥑")
s.get(5..6) // => Some("B")
- 範囲でしか指定できない
- Rustではマルチバイト文字を含む文字列の操作が難しそうだ
chars
→ chars
Ruby
"Aあ🥑".chars # => ["A", "あ", "🥑"]
"Aあ🥑".codepoints # => [65, 12354, 129361]
Rust
"Aあ🥑".chars().collect::<Vec<_>>() // => ['A', 'あ', '🥑']
文字単位で扱う場合はさっさとこれで配列化してから操作した方がよさそう
size
→ chars.count
Ruby
"🥑".size # => 1
Rust
"🥑".chars().count() // => 1
文字数
to_i
→ parse::<isize>()
Ruby
"567".to_i # => 567
Rust
"567".parse::<isize>() // => Ok(567)
let v: isize = "567".parse().unwrap();
v // => 567
" 567 ".parse::<isize>() // => Err(ParseIntError { kind: InvalidDigit })
" 567 ".trim().parse::<isize>() // => Ok(567)
-
::<xxx>
の部分をターボフィッシュというらしい- 戻値を受け取る変数に型指定があれば省略できる
- parse の方に指定した方がわかりやすい
- 空白や改行が含まれているだけで
InvalidDigit
と言われる-
trim()
とペアで使おう
-
lines(chomp: true)
→ lines
Ruby
"foo\nbar\nbaz\n".lines(chomp: true) # => ["foo", "bar", "baz"]
Rust
"foo\nbar\nbaz\n".lines().collect::<Vec<_>>() // => ["foo", "bar", "baz"]
strip
→ trim
Ruby
" foo ".strip # => "foo"
Rust
" foo ".trim() // => "foo"
lstrip
→ trim_start
Ruby
" foo ".lstrip # => "foo "
Rust
" foo ".trim_start() // => "foo "
rstrip
→ trim_end
Ruby
" foo ".rstrip # => " foo"
Rust
" foo ".trim_end() // => " foo"
split
→ split_whitespace
Ruby
" a\r\n b c\n".gsub(/[[:space:]]+/, " ").split # => ["a", "b", "c"]
Rust
" a\r\n b c\n".split_whitespace().collect::<Vec<_>>() // => ["a", "b", "c"]
- 全角スペースもスペースとして扱う
split(x)
→ split(x)
Ruby
"a-b-c".split("-") # => ["a", "b", "c"]
"a-b_c".split(/[-_]/) # => ["a", "b", "c"]
Rust
"a-b-c".split('-').collect::<Vec<_>>() // => ["a", "b", "c"]
"a-b-c".split(|e| e == '-').collect::<Vec<_>>() // => ["a", "b", "c"]
"a-b_c".split(['-', '_']).collect::<Vec<_>>() // => ["a", "b", "c"]
gsub!(str, "")
→ remove_matches(str)
Ruby
s = "foo_bar"
s.gsub!("bar", "")
s # => "foo_"
Rust (nightly)
let mut s = String::from("foo_bar");
s.remove_matches("bar");
s // => "foo_"
concat(str)
→ push_str(str)
Ruby
s = "foo"
s.concat("bar")
s # => "foobar"
Rust
let mut s = String::from("foo");
s.push_str("bar");
s // => "foobar"
メソッド名がイケてない
concat(ch)
→ push(ch)
Ruby
s = "foo"
s.concat('b')
s.concat('a')
s.concat('r')
s # => "foobar"
Rust
let mut s = String::from("foo");
s.push('b');
s.push('a');
s.push('r');
s // => "foobar"
こんな滅多に使わなそうなのは push_char にして push_str を push にしてほしかった
self * n
→ repeat(n)
Ruby
"foo" * 2 # => "foofoo"
Rust
"foo".repeat(2) // => "foofoo"
clear
→ clear
Ruby
s = "foo"
s.clear
s # => ""
Rust
let mut s = String::from("foo");
s.clear();
s // => ""
empty?
→ is_empty
Ruby
"".empty? # => true
Rust
"".is_empty() // => true
include?
→ contains
Ruby
"abcd".include?("bc") # => true
Rust
"abcd".contains("bc") // => true
chars.each.with_index
→ char_indices
Ruby
"Aあ🥑".chars.each.with_index.entries # => [["A", 0], ["あ", 1], ["🥑", 2]]
Rust
"Aあ🥑".char_indices().collect::<Vec<_>>() // => [(0, 'A'), (1, 'あ'), (4, '🥑')]
- Rust のほうの位置は連番ではない
- バイト配列での位置になっている
inspect
→ escape_debug
Ruby
"A🥑\n".inspect # => "\"A🥑\\n\""
Rust
"A🥑\n".escape_debug().to_string() // => "A🥑\\n"
改行がエスケープされ \n
表記になる
gsub
→ replace
Ruby
"abcabcabc".gsub("ab", "__") # => "__c__c__c"
Rust
"abcabcabc".replace("ab", "__") // => "__c__c__c"
gsub 最初のn回だけ
→ replacen
Ruby
# もっとましな方法ありそう
n = 2
c = 0
s = "abcabcabc".gsub("ab") do |m|
c += 1
if c <= n
"__"
else
m
end
end
s # => "__c__cabc"
Rust
"abcabcabc".replacen("ab", "__", 2) // => "__c__cabc"
insert(i, str)
→ insert_str(i, str)
Ruby
s = "foo"
s.insert(1, "__")
s # => "f__oo"
Rust
let mut s = String::from("foo");
s.insert_str(1, "__"); // 文字列
s // => "f__oo"
メソッド名がイケてない
insert(i, ch)
→ insert(i, ch)
Ruby
s = "foo"
s.insert(1, "_")
s # => "f_oo"
Rust
let mut s = String::from("foo");
s.insert(1, '_'); // 文字
s // => "f_oo"
new
→ new
Ruby
s = String.new
s # => ""
Rust
let s = String::new();
s // => ""
bytes
→ into_bytes
Ruby
"abc".bytes # => [97, 98, 99]
Rust
String::from("abc").into_bytes() // => [97, 98, 99]
所有権が移動するやつ?
bytes
→ as_bytes
Ruby
"abc".bytes # => [97, 98, 99]
Rust
"abc".as_bytes() // => [97, 98, 99]
b"abc" // => [97, 98, 99]
型変換? 表記がアレだけど b
をつけても同じ結果になる
bytes
→ as_mut_vec
Ruby
"abc".bytes # => [97, 98, 99]
Rust
let mut s = String::from("abc");
let v = unsafe { s.as_mut_vec() };
v // => [97, 98, 99]
同じようなのがいくつもあるな
slice!(n..)
→ truncate(n)
Ruby
s = "foobar"
s.slice!(3..) # => "bar"
s # => "foo"
s = "A🥑B"
s.slice!(2..) # => "B"
s # => "A🥑"
Rust
// truncate した部分を返したりはしない
let mut s = String::from("foobar");
s.truncate(3) // => ()
s // => "foo"
// パニックになる
// let mut s = String::from("A🥑B");
// s.truncate(2)
マルチバイト文字が含まれていたら文字の境界線を慎重に指定しないとパニックになる
slice!(-1)
→ pop
Ruby
s = "foobar"
s.slice!(-1) # => "r"
s # => "fooba"
Rust
let mut s = String::from("foobar");
s.pop() // => Some('r')
s // => "fooba"
slice!(i)
→ remove(i)
Ruby
s = "foobar"
s.slice!(3) # => "b"
s # => "fooar"
Rust
let mut s = String::from("foobar");
s.remove(3) // => 'b'
s // => "fooar"
select! 類似
→ retain
Ruby
class String
def retain(&block)
replace(each_char.select(&block).join)
end
end
s = "f_o_o"
s.retain { |e| e != "_" }
s # => "foo"
s = "f_o_o"
s.delete!("_")
s # => "foo"
Rust
let mut s = String::from("f_o_o");
s.retain(|e| e != '_'); // "_" では文字列を表すのでコンパイルエラー
s // => "foo"
この例では remove_matches("_")
のほうが良い
slice!(i..)
→ split_off(i)
Ruby
s = "foo"
s.slice!(1..) # => "oo"
s # => "f"
s = "foo"
s.byteslice(1...) # => "oo"
s.replace(s.byteslice(...1))
s # => "f"
Rust
let mut s = String::from("foo");
s.split_off(1) // => "oo"
s // => "f"
正確には byteslice の破壊版に近い
[range] = v
→ replace_range(range, v)
Ruby
s = "abcd"
s[...2] = "__"
s # => "__cd"
Rust
let mut s = String::from("abcd");
s.replace_range(..2, "__");
s // => "__cd"
slice
→ get_mut
Ruby
"foo".slice(0..1).upcase # => "FO"
Rust
let mut s = String::from("foo");
let s = s.get_mut(0..=1);
let s = s.map(|e| { // e は "fo"
e.make_ascii_uppercase();
&*e
});
s // => Some("FO")
なんだこれ
split(x).reverse
→ rsplit(x)
Ruby
"a-b-c".split("-").reverse # => ["c", "b", "a"]
Rust
"a-b-c".rsplit('-').collect::<Vec<_>>() // => ["c", "b", "a"]
split(/(?<=x)/)
→ split_inclusive(x)
Ruby
"a-b-c".split(/(?<=-)/) # => ["a-", "b-", "c"]
Rust
"a-b-c".split_inclusive('-').collect::<Vec<_>>() // => ["a-", "b-", "c"]
セパレータで分けて前の要素に残す
split(x)
→ split_terminator(x)
Ruby
"a-b-c-".split("-") # => ["a", "b", "c"]
Rust
"a-b-c-".split_terminator("-").collect::<Vec<_>>() // => ["a", "b", "c"]
セパレータではなく句点のように終端に特定の文字がある構造を分ける
split(x).reverse
→ rsplit_terminator(x)
Ruby
"a-b-c-".split("-").reverse # => ["c", "b", "a"]
Rust
"a-b-c-".rsplit_terminator("-").collect::<Vec<_>>() // => ["c", "b", "a"]
split(sep, n)
→ splitn(n, sep)
Ruby
"foo-bar-baz".split("-", 2) # => ["foo", "bar-baz"]
Rust
"foo-bar-baz".splitn(2, "-").collect::<Vec<_>>() // => ["foo", "bar-baz"]
split 末尾から
→ rsplitn(n, sep)
Ruby
"foo-bar-baz".reverse.split("-", 2).collect(&:reverse) # => ["baz", "foo-bar"]
Rust
"foo-bar-baz".rsplitn(2, "-").collect::<Vec<_>>() // => ["baz", "foo-bar"]
split(sep, 2)
→ split_once(sep)
Ruby
"foo-bar-baz".split("-", 2) # => ["foo", "bar-baz"]
Rust
"foo-bar-baz".split_once("-") // => Some(("foo", "bar-baz"))
split(sep, 2) 末尾から
→ rsplit_once(sep)
Ruby
s = "foo-bar-baz".reverse.split("-", 2)
s.collect(&:reverse).reverse # => ["foo-bar", "baz"]
Rust
"foo-bar-baz".rsplit_once("-") // => Some(("foo-bar", "baz"))
scan
→ matches
Ruby
"_56_".scan(/\d/) # => ["5", "6"]
Rust
"_56_".matches(char::is_numeric).collect::<Vec<_>>() // => ["5", "6"]
予想に反して1文字づつ返ってくる
reverse.scan
→ rmatches
Ruby
"_56_".reverse.scan(/\d/) # => ["6", "5"]
Rust
"_56_".rmatches(char::is_numeric).collect::<Vec<_>>() // => ["6", "5"]
start_with?
→ starts_with
Ruby
"abcd".start_with?("ab") # => true
Rust
"abcd".starts_with("ab") // => true
end_with?
→ ends_with
Ruby
"abcd".end_with?("cd") # => true
Rust
"abcd".ends_with("cd") // => true
ascii_only?
→ is_ascii
Ruby
"foo".ascii_only? # => true
Rust
"foo".is_ascii() // => true
index
→ find
Ruby
s = "abcdabcd"
s.index("c") # => 2
s.index("cd") # => 2
s.chars.index { |e| e == 'c' } # => 2
Rust
let s = "abcdabcd";
s.find('c') // => Some(2)
s.find("cd") // => Some(2)
s.find(|e| e == 'c') // => Some(2)
xxx, xxx_by, xxx_by_key シリーズのように引数の型が変わるたびに異なるメソッドになるのが Rust 流かと思いきや、このメソッドはいろんな引数に対応していてありがたい
rindex
→ rfind
Ruby
s = "abcdabcd"
s.rindex("c") # => 6
s.rindex("cd") # => 6
s.chars.rindex { |e| e == 'c' } # => 6
Rust
let s = "abcdabcd";
s.rfind('c') // => Some(6)
s.rfind("cd") // => Some(6)
s.rfind(|e| e == 'c') // => Some(6)
strip 類似
→ trim_matches
Ruby
"56foo78".sub(/\A\d+(.*?)\d*\z/, '\1') # => "foo"
Rust
"56foo78".trim_matches(char::is_numeric) // => "foo"
lstrip 類似
→ trim_start_matches
Ruby
"56foo78".sub(/\A\d+/, "") # => "foo78"
Rust
"56foo78".trim_start_matches(char::is_numeric) // => "foo78"
rstrip 類似
→ trim_end_matches
Ruby
"56foo78".sub(/\d+\z/, "") # => "56foo"
Rust
"56foo78".trim_end_matches(char::is_numeric) // => "56foo"
delete_prefix
→ strip_prefix
Ruby
"56foo56".delete_prefix("56") # => "foo56"
Rust
"56foo56".strip_prefix("56") // => Some("foo56")
delete_suffix
→ strip_suffix
Ruby
"56foo56".delete_suffix("56") # => "56foo"
Rust
"56foo56".strip_suffix("56") // => Some("56foo")
casecmp?(other)
→ eq_ignore_ascii_case
Ruby
"Föö".casecmp?("föö") # => true
Rust
"Föö".eq_ignore_ascii_case("föö") // => true
upcase
→ to_uppercase
Ruby
"aAöÖ❤".upcase # => "AAÖÖ❤"
Rust
"aAöÖ❤".to_uppercase() // => "AAÖÖ❤"
downcase
→ to_lowercase
Ruby
"aAöÖ❤".downcase # => "aaöö❤"
Rust
"aAöÖ❤".to_lowercase() // => "aaöö❤"
tr("a-z", "A-Z")
→ to_ascii_uppercase
Ruby
"aAöÖ❤".tr("a-z", "A-Z") # => "AAöÖ❤"
Rust
"aAöÖ❤".to_ascii_uppercase() // => "AAöÖ❤"
tr("A-Z", "a-z")
→ to_ascii_lowercase
Ruby
"aAöÖ❤".tr("A-Z", "a-z") # => "aaöÖ❤"
Rust
"aAöÖ❤".to_ascii_lowercase() // => "aaöÖ❤"
tr!("a-z", "A-Z")
→ make_ascii_uppercase
Ruby
s = "aAöÖ❤"
s.tr!("a-z", "A-Z")
s # => "AAöÖ❤"
Rust
let mut s = String::from("aAöÖ❤");
s.make_ascii_uppercase();
s // => "AAöÖ❤"
tr!("A-Z", "a-z")
→ make_ascii_lowercase
Ruby
s = "aAöÖ❤"
s.tr!("A-Z", "a-z")
s # => "aaöÖ❤"
Rust
let mut s = String::from("aAöÖ❤");
s.make_ascii_lowercase();
s // => "aaöÖ❤"
?
→ match_indices
Ruby
"_56_".chars.filter_map.with_index { |e, i| [i, e] if e.match?(/\d/) } # => [[1, "5"], [2, "6"]]
Rust
"_56_".match_indices(char::is_numeric).collect::<Vec<_>>() // => [(1, "5"), (2, "6")]
?
→ rmatch_indices
Ruby
"_56_".chars.filter_map.with_index { |e, i| [i, e] if e.match?(/\d/) }.reverse # => [[2, "6"], [1, "5"]]
Rust
"_56_".rmatch_indices(char::is_numeric).collect::<Vec<_>>() // => [(2, "6"), (1, "5")]
dump 一部unicode
→ escape_default
Ruby
"A🥑\n".dump # => "\"A\\u{1F951}\\n\""
Rust
"A🥑\n".escape_default().to_string() // => "A\\u{1f951}\\n"
- 絵文字はユニコードの数字表記になる
- ちょうど良い
dump 全部unicode
→ escape_unicode
Ruby
"A🥑\n".dump # => "\"A\\u{1F951}\\n\""
Rust
"A🥑\n".escape_unicode().to_string() // => "\\u{41}\\u{1f951}\\u{a}"
- 全部ユニコードの数字表記になる
- 改行も a になって余計わからん
to_s
→ to_string
Rust
"foo".to_string() // => "foo"
to_s
→ as_str
Ruby
"abc".to_s # => "abc"
Rust
String::from("abc").as_str() // => "abc"
- String型にしか生えてない
- to_string と何が違う?
to_s 更新用
→ as_mut_str
Ruby
"abc".to_s # => "abc"
Rust
let mut x = String::from("abc");
let s = x.as_mut_str(); // let mut と書かなくていいのかな?
s.make_ascii_uppercase();
s // => "ABC"
// これと同じだけど、どういうこと???
let mut s = String::from("abc");
s.make_ascii_uppercase();
s // => "ABC"
なんかよくわからないけどそのうちわかるだろう
ary.pack("C*")
→ from_utf8(ary)
Ruby
puts [240, 159, 144, 178].pack("C*")
# >> 🐲
Rust
String::from_utf8(vec![240, 159, 144, 178]) // => Ok("🐲")
ary.pack("C*").scrub
→ from_utf8_lossy(ary)
Rust
let v = vec![102, 111, 111, 129, 98, 97, 114];
String::from_utf8_lossy(&v) // => "foo�bar"
129 は無効な文字なので � になる
new(capacity: xxx)
→ with_capacity(xxx)
Ruby
s = String.new(capacity: 65535)
s # => ""
# capacity 見れんの?
Rust
let s = String::with_capacity(65535);
s // => ""
s.capacity() // => 65535
?
→ reserve(size)
Rust
let mut s = String::from("foo");
s.capacity() // => 3
s.reserve(65535);
s.capacity() // => 65538
- あとから容量を確保する
- キリの良い単位で確保するので指定よりも多く確保することもある
?
→ shrink_to_fit
Rust
let mut s = String::from("foo");
s.capacity() // => 3
s.reserve(65535);
s.capacity() // => 65538
s.shrink_to_fit();
s.capacity() // => 3
reserve の逆でメモリを切り詰める
?
→ into_raw_parts
Rust (nightly)
let s = String::from("foo");
let (ptr, len, cap) = s.into_raw_parts();
ptr // => 0x7feb59405eb0
len // => 3
cap // => 3
文字列をさらにラップしているらしい
?
→ from_raw_parts
Rust
use std::mem;
let s = String::from("foo");
let mut s = mem::ManuallyDrop::new(s);
s // => ManuallyDrop { value: "foo" }
let ptr = s.as_mut_ptr();
let len = s.len();
let cap = s.capacity();
ptr // => 0x7f80f4c05eb0
len // => 3
cap // => 3
unsafe {
let s = String::from_raw_parts(ptr, len, cap);
s // => "foo"
}
自力で文字列作成用?
?
→ into_boxed_str
Rust
let s = String::from("foo");
let x = s.into_boxed_str(); // s から x に所有権移動しちゃってる
x // => "foo"
どゆこと?
?
→ is_char_boundary
Rust
let s = "A🥑B";
s.is_char_boundary(0) // => true
s.is_char_boundary(1) // => true
s.is_char_boundary(2) // => false
s.is_char_boundary(3) // => false
s.is_char_boundary(4) // => false
s.is_char_boundary(5) // => true
s.is_char_boundary(6) // => true
指定位置のバイトがUTF-8コードポイントシーケンスの最初のバイトまたは文字列の終わりなら true らしいがピンとこない
?
→ as_ptr
Rust
let s = "foo";
s.as_ptr() // => 0x1020434a0
ptr はC言語で言うところポインタなのかな
?
→ as_mut_ptr
Rust
let mut s = String::from("foo");
s.as_mut_ptr() // => 0x7ff0f0405eb0
文字を更新するとき用
Discussion