関連

変数は主にlet 変数名 = 値;という文で定義します。

let foo = "foo";
let thirty_three = 33;
let truthy = true;

型は Rust がその場の記注などから自動で付与してくれる為省略できる場合があります。とはいえ型を付けても問題ありません。

let foo: &str = "foo";
let thirty_three: i32 = 33;
let truthy: bool = true;

記注することで型それに合わせてくれる場合があります。以下ではthirty_threeu32型になります。

let thirty_three: u32 = 33;

また例えばStringVec<u8>Box<str>を実装済みの為以下のような事もできます。

let ref_str: Vec = "foo".to_string().into();
// ref_str は `std::vec::Vec`
let ref_str: Box = "foo".to_string().into();
// ref_str は `std::boxed::Box`

Rust では結果の型に合わせて::intoのように処理が変わる事が良くあります。

タプルでまとめて定義

タプルは( )で囲みます。関数などから複数の値を同時に返すなどもできます。

let (nju, thirty_three): (&str, i32) = ("nju", 33);

変更可能な変数

letのあとにmutと続けると変数に再代入したり、

let mut value = "foo";
value = "baz";

assert_eq!(value, "baz");

編集できるようになります。

let mut num = 33;
num += 1;

assert_eq!(num, 34);

あとで代入する変数

宣言だけした場合、mutを付けなくてもあとで値を代入できます。

let value;

value = 33;

assert_eq!(value, 33);

使わない変数

下記の状態の時、Rust はfooに対してwarning: unused variable: fooのような警告を発行します。

let foo = "foo";
// 以後 `foo` を使っていない

使わないけど定義したい変数には_を頭に付けるか、_という名前で定義すると許されます。

let _foo = "foo";

関数の引数などにも使えます。

fn do_something(_: &str) {
  // snap
}

借用ルール

所有を借用する時には守らなければならない2つのルールがあります。

  1. 不変な借用であればいくらでも借用できる

  2. 可変な借用がある場合、それ以上借用できない

1つ目は以下のコードのように参照をいくつもの変数に持つ事ができます。

let mut num = 33;
let ref1 = #
let ref2 = #
let ref3 = #

println!("{}, {}, {}", ref1, ref2, ref3);
// 33, 33, 33

2つ目は以下のように&mut Tが既にあるスコープにさらに&Tがあるとエラーになります。

let mut num = 33;
let ref1 = &mut num;
let ref2 = &mut num;

println!("{}, {}", ref1, ref2);
// error[E0499]: cannot borrow `num` as mutable more than once at a time

定数

これにはletの代わりにconstを使います。プログラム全体の中で何度か使われる値をその度定義して使うのは良くないです。

const FOO: &str = "foo";

fn main() {
  println!("{}", FOO);
}
関連

文字列はダブルクォートで書こんで定義します。これで定義した値の型はプリミティブなstrになります。これは定義時に省略できます。

let str = "foo";
// let str: &'static str = "foo";
// と同等

println!("{}", str); // "foo"

Rust の文字列はそのままで改行を含ませられます。

let str = "aiueo
kakikukeko";

もし文字列にエスケープ文字が含まれ、それをそのまま表示したい場合r" "で囲みます。

let str = r"¯\_(ツ)_/¯";

さらにダブルクォートを含ませたい場合は、r#" "#で囲みます。

let str = r#""#;

この(UTF-8)文字列のサイズは固定ですが、可変にしたい時用に Struct std::string::String も提供されています。この型へはstrから変換することができます。

let string = str.to_string();
let string2 = String::from(str);

println!("{}, {}", string, string2);

文字列結合

文字列を結合するには最初をString型にします。

let s = "foo".to_string() + "bar" + "baz";

assert_eq!(s, "foobarbaz");

既にStringの場合、後に来るものを参照で使います。

let foo = "foo".to_string();
let bar = "bar".to_string();
let baz = "baz".to_string();
let s = foo + &bar + &baz;

assert_eq!(s, "foobarbaz");

またはformat!マクロを使って好きな位置に適当な変数を埋め込む方法もあります。

let s = format!("{1}{0}{baz}", "bar", "foo", baz = "baz");
assert_eq!(s, "foobarbaz");

元の変数を書き換えるならpush_strpushが使えます。

let mut s = String::new();

s.push_str("foo");
s.push('b');
s.push('a');
s.push('r');

assert_eq!(s, "foobar");

文字数の取得

StringlenはUTF-8でのバイト単位のサイズを返します。

"foo".len(); // 3
"あいうえお".to_string().len(); // 15

"foo"のような1バイト文字だけの場合は文字数と一致したサイズが取得できますが、"あいうえお"のような1文字あたり3バイトで表示されるものの場合は3 x 5 = 15となってしまいます。

2,3,4バイトの文字が含まれている場合でも正確な文字数を取得したい場合は、chars().count()を使います。これはstrStringどちらでも使えます。

"foo".chars().count(); // 3
"あいうえお".to_string().chars().count(); // 5

空文字列かどうかはis_emptyで調べることができます。

"".is_empty(); // true
関連

ダブルクォオート"ではなくシングルクォート'で囲むと Primitive Type char を作れます。

let a = 'a';
let b: char = 'b';

文字が何バイトか調べる

::len_utf8を使います。

assert_eq!('純'.len_utf8(), 3);

文字が大文字か小文字か調べる

それぞれ::is_uppercase::is_lowercaseで調べられます。

assert!('A'.is_uppercase());
assert!('a'.is_lowercase());
関連

数値のプリミティブ型は以下のものがあります。

f32f64以外はmin_valuemax_valueメソッドにより取得・確認できます。

(i8::min_value(), i8::max_value())
// (-128, 127) (-2^7, 2^7 - 1)
(i16::min_value(), i16::max_value())
// (-32768, 32767) (-2^15, 2^15 - 1)
(i32::min_value(), i32::max_value())
// (-2147483648, 2147483647) 
(i64::min_value(), i64::max_value()) 
// (-9223372036854775808, 9223372036854775807)
(i128::min_value(), i128::max_value())
// (-170141183460469231731687303715884105728, 170141183460469231731687303715884105727)
(u8::min_value(), u8::max_value())
// (0, 255) (0, 2^8 - 1)
(u16::min_value(), u16::max_value())
// (0, 65535) (0, 2^16 - 1)
(u32::min_value(), u32::max_value())
// (0, 4294967295) 
(u64::min_value(), u64::max_value())
// (0, 18446744073709551615) 
(u128::min_value(), u128::max_value())
// (0, 340282366920938463463374607431768211455)
(isize::min_value(), isize::max_value())
// (-9223372036854775808, 9223372036854775807)
(usize::min_value(), usize::max_value())
// (0, 18446744073709551615)

f32f64はそれぞれi32i64の範囲内で小数点有りの数値が持てます。

宣言

変数宣言時に希望する型を指定するか、型名を数値の後ろに付けることで希望する型の値が得られます。デフォルトは整数の場合i32、少数以下が有る場合f32になります。

let a: i8 = 1; // i8
let b = 12i16; // i16
let c = 123; // i32

値を足す

Trait std::ops::Addが上記の型すべてに実装されているので、それぞれで+が使えます。

10 + 20 // 30i32
4.5 + 5.5 // 10.0f32

値を順番に足していきたいような場合、Trait std::ops::AddAssignによる+=が使えます。

let mut num = 1;
num += 2;
assert_eq!(num, 3);

値を引く

Trait std::ops::Subが上記の型すべてに実装されているので、それぞれで-が使えます。

30 - 10 // 20i32
10.0 - 4.5 // 5.5f32

値を順番に引きていきたいような場合Trait std::ops::SubAssignによる-=が使えます。

let mut num = 3;
num -= 2;
assert_eq!(num, 1);

値を掛ける

これまでのようにTrait std::ops::Mulにより*、またTrait std::ops::MulAssignによる*=が実装されています。

2 * 3 // 6i32
2.0 * 3.0 // 6.0f32
let mut num = 2;
num *= 3;
assert_eq!(num, 6);

値を割る

これまでのようにTrait std::ops::Divにより/、またTrait std::ops::DivAssignによる/=が実装されています。

値が割り切れなかった場合、f32f64以外では小数点以下は切り捨てされます。

5 / 4 // 1i32
5.0 / 4.0 // 1.25.0f32
let mut num = 6.0;
num /= 4.0;
assert_eq!(num, 1.5f32);
関連

他の言語と同じようにtruefalseが用意されています。これはif構文により条件によって処理を分岐できます。

if (true) { 1 } else { 2 } // 1
if (false) { 1 } else { 2 } // 2

値の反転

boolean型の前に!を付けると反対の意味になります。

!true // false
!false // true
関連

Option<T>は値があるないかを表現する為の型です。ある場合はSome<T>、ない場合はNoneになります。

let option_thirty_three = Some(33);
let none = None as Option;

Optionな値は基本中身を取り出してから使います。そのⅠつが::unwrapです。これは値がSome<T>の時Tを取り出してくれます。

assert_eq!(Some(33).unwrap(), 33);

ただし、これは値が入ってるので大丈夫ですがNoneに対して呼んでしまうと Panic を起こしてしまいます。

assert_eq!((None as Option).unwrap(), 33);
// thread 'main' panicked at 'called `Option::unwrap()`
// on a `None` value'

Noneの時はデフォルト値を返したいなどの場合は、::unwrap_orを使います。

assert_eq!((None as Option).unwrap_or(66), 66);

もしくは Panic のままエラー文章だけ分かりやすくしたい場合は、::expectを使います。

assert_eq!((None as Option).expect("a value isn't equal 33"), 33);
// thread 'main' panicked at 'a value isn't equal 33'

値の加工

Optionのまま値を更新できるのが::mapです。

assert_eq!(
  Some(33).map(|mut thirty_three| {
    thirty_three += 1;
    thirty_three
  }),
  Some(34)
);
関連

Result<T, E>は処理が無事完了したかを表現する為の型です。正常に終了した場合Ok<T>、何かしらエラーが起きた場合Err<E>を返します。

let ok: Result = Ok(33);
let err: Result = Err("fatal error");

Result<T, E>はモジュールなどに合わせてエイリアス化されている事が多いです。エイリアスにはtypeを使います。

enum FooError {
  AError,
  BError,
}

type FooResult = Result;

? 演算子

Resultを返す関数呼び出しの後ろに?を置くと、Ok<T>の時は::unwrapのようにTを返しますが、Err<E>が返却された時は即時returnになります。

fn return_ok<'a>() -> Result<&'a str, &'a str> {
  Ok("foo")
}

fn scope<'a>() -> Result<&'a str, &'a str> {
  let foo = return_ok()?;

  println!("{}", foo);

  Ok(foo)
}

fn main() {
  #[allow(unused_must_use)]
  scope();
}

return_okResult型を返す為だけの関数で、scopeは単にそれを実行する為の関数です。scopeを用意した理由は、?は戻り値がResultな関数の中でしか使えません。

また?Err<E>な時、単にそれを返すのではなくFrom::fromEを渡して実行した結果を返します。なので以下のようにエラー列挙型にまとめるといった事ができます。

struct FooError;
struct BarError;

#[derive(Debug, PartialEq, Eq)]
enum AError<'a> {
  FooError(&'a str),
  BarError(&'a str),
}

impl<'a> From for AError<'a> {
  fn from(_error: FooError) -> Self {
    AError::FooError("FooError")
  }
}

impl<'a> From for AError<'a> {
  fn from(_error: BarError) -> Self {
    AError::BarError("BarError")
  }
}

fn return_err<'a>() -> Result<&'a str, FooError> {
  Err(FooError)
}

fn scope<'a>() -> Result<&'a str, AError<'a>> {
  let _ = return_err()?;

  unreachable!();
}

fn main() {
  assert_eq!(scope().unwrap_err(), AError::FooError("FooError"));
}

return_errは必ずエラーを返す関数です。impl<'a> From<FooError> for AError<'a>によってFooErrorからAErrorに変換できるようにしてる為、この場合AError::FooErrorEとして返ります。

パターンマッチには複数の真マッチ条件を持つmatchと1つの真マッチ条件を持つif letがあります。

複数の条件分岐

match構文を使うと多くのケースに対応できます。

単純にイコールならその行の=>より右のブロックや式が実行されます。

match 1 {
  1 => println!("foo"),
  2 => println!("bar"),
  _ => println!("baz"),
}
// foo

ちなみに、_行はどれにもマッチしなかった場合に実行されます

match 10 {
  1 => println!("foo"),
  2 => println!("bar"),
  _ => println!("baz"),
}
// baz

値も返せます。

let val = match 1 {
  1 => "foo",
  2 => "bar",
  _ => "baz",
};
assert_eq1(val, "foo");

matchでは必ず条件のどれかとマッチしなければなりません。どれともマッチしない可能性が有る場合エラーとなります。

match 10 {
  1 => println!("foo"),
}
// non-exhaustive patterns: `_` not covere

matchでは完全なイコールだけでなく、同じ形ならマッチしてくれる点が強力です。例えばOption<T>は値があればSome<T>、無ければNoneとなる型です。この型な値Some(33)matchに渡した時、パターンにSome(num)と書けばそれにマッチしてくれます。そして、なんとそのnumlet num = 33のように変数束縛されているので、その後の処理で使うことができます!

match Some(33) {
  Some(num) => println!("Some({})", num),
  None => println!("None"),
}

以下はNoneにマッチです。

match None as Option {
  Some(num) => println!("Some({})", num),
  None => println!("None"),
}

パターンの後にifと条件を書くことで、さらにその条件がtrueの時にだけ走らせられます。

let x: i32 = 33;

match Some(x) {
  Some(num) if num == 33 => println!("the x equals 33"),
  _ => println!("None"),
}
// "the x equals 33"

パターンは|で区切ることで複数指定できます。

let x: i32 = 33;
let value = AB::B { value: x };

match value {
  AB::A(num) | AB::B { value: num } if num == 33 => println!("the x equals 33"),
  _ => println!("None"),
}
// "the x equals 33"

1つのの条件分岐

以下はSome(33)Some(num)とマッチします。if letでもマッチした時だけその後のブロック処理を走らせることができます。ちなみに、このブロックの戻り値は()である必要があります。

if let Some(num) = Some(33) {
  println!("it has walked along truthy with {}", num);
}
// it has walked along truhy with 33

もし、マッチしなかった場合の為にelseブロックも用意されてます。

if let Some(num) = None as Option<()> {
  unreachable!();
} else {
  println!("it has walked along falsy");
}
// it has walked along falsy

配列arrayは固定サイズで所有権を持つ値で、スライスはその参照です。

配列は[ ]に囲む形で定義しますが、中身の書き方には2つの方法があります。1つ目は,で区切って好きな値を列挙する方法、2つ目はある値をまず置き、その後;で区切った後リピート回数を置く方法です。これは例えば[0; 3]と書くと[0, 0, 0]になることを意味します。
後者の場合、基準となる値(前の例で言う0)はCopyできる必要があります。

{
  let array = [1, 2, 3];
  let _slice = &array;
}

{
  let array = [0; 3];
  let _slice = &array;
}

for で使う

IteratorまたはIntoIteratorを持つ要素にする必要があります。array::iterを持っている為これを使うか、arrayは&'a [T; N]&'a mut [T; N]に対してIntoIteratorを実装しているのでスライス化すればfor構文で回せるようになります。

for i in [1, 2, 3].iter() {
  println!("{}", i);
  // `1..=3`まで表示
}

for i in &[1, 2, 3] {
  println!("{}", i);
  // `1..=3`まで表示
}
関連

ベクター(Struct std::vec::Vec)を楽に構築するvec!マクロにより拡張可能なコレクションを定義できます。

[ ]で囲むということは共通ルールですが、これには2つの構文があり1つは,で区切って値を定義する方法です。

let v = vec![1, 2, 3];
// [1, 2, 3]

2つはすべての値が同じ時に便利で共通値; サイズのように記述します。例えば、1を3つ持ったベクターを定義したい場合はこうなります。

let v = vec![1; 3];
// [1, 1, 1]

尾に値を追加

pushメソッドを使えます。

let mut v = vec![1, 2];
v.push(3);

assert_eq!(v, vec![1, 2, 3]);

インデックス値を取得

最後の値を取得したい場合popメソッドが使えます。戻り値はVec<T>であればOption<T>になります。

let mut v = vec![1, 2, 3];
let option_i32 = v.pop();

assert_eq!(option_i32, Some(3i32));

好きなインデックス値を削除したいならremoveメソッドが使えます。このメソッドの戻り値はその位置にあった値です。もし、インデックス値がベクターサイズより大きい値の場合panic!されます。

let mut v = vec![1, 2, 3];
let removed_i32 = v.remove(0);

assert_eq!(removed_i32, 1i32);
assert_eq!(v, vec![2, 3]);

// v.remove(5); は `panic!`

サイズを取得

lenメソッドを使います。

let v = vec![1; 3];
v.len() // 3

空がどうかを確認したいだけならis_empty(&self) -> boolが便利です。

JavaScript で飯食べたい歴約 5 年、 純( nju33 ) によるノートサイトです。

このサイトではドリンク代や奨学金返済の為、広告などを貼らせて頂いてますがご了承ください。

Change Log