Learning Rust - Misc.

Syntax and Semantics

Rustのsyntaxとsemanticsについて、いくつか細かい事項をまとめて紹介します。

const and static

Const変数とstatic変数は、定数を記述するためのものです。 letでもmutをつけなければ定数ですが、const, static変数はグローバルに宣言できます。

const

constは、コンパイル時定数です。

const N: i32 = 5;

letと違って、型を明示する必要があります。 Constな変数は、それを使うところにinline展開されます。 つまり、Nはすべて5に置き換えられるということです。 よって、constな変数はメモリ上に配置されたりしません。

一方、staticはメモリ上に置かれ、'static lifetimeをもちます。 よって、static変数へのアクセスは、すべておなじメモリ上の値へのアクセスになります。

あたり前のことですが、const, static変数ともに、初期値が必要です。 実は、static変数はmutをつければmutableになります。 ただし、mutableなstatic変数へのアクセスは安全性が保証されないので、 unsafeブロック内でおこなう必要があります。 使わないに越したことはない、ということですね。

定数を使いたいほとんどの場合はconstを使っておくのがよさそうです。

Attributes

これまでも#[derive(Debug)]とかが登場していましたが、これを’attribute’といいます。 コードになんらかの性質をもたせるものです。

Attributeの書きかたには二種類あり、

#[foo]
struct Foo;

mod bar {
    #![bar]
}

のように、!をつけない#[foo]と、つける#![bar]があります。 !をつけないほうは、そのattributeの直後のものについて作用するのに対し、 !をつけるほうは、そのattributeがかかれているもの(ここではmod bar)について作用します。 違いはこれだけです。

どんなattributeがあるのかというと、たとえば

#[test]
fn check() {
    assert_eq!(2, 1 + 1);
}

というのがあります。 関数に#[test]attributeをつけると、その関数はテスト用の関数となり、 テスト時に実行されます。

他には、inline展開を指定する(もっともinline展開の判断はコンパイラに任せるべきですが)

#[inline(always)]
fn super_fast_fn() {

や、コンパイル条件を設定する

#[cfg(target_os = "macos")]
mod macos_only {

というのがあります。 ほかのattributeについては、 公式リファレンス を読むのがいいでしょう。

type aliases

typeは型に別名をつけるものです。 C++でいうusingエイリアスです。 以下のように使います。

type Name = String;

let x: Name = "Hello".to_string();

似たようなものに、要素が一つのtuple structがありました。 要素が一つのtuple structは、別の型をつくった(newtype)のに対し、 typeエイリアスはただの別名です。

Casting Between Types

Rustは強い静的型付き言語ですが、型変換もできます。 方法は二通りあります。

as

asキーワードをつかうと、ちゃんと型変換できるかコンパイラがチェックしてくれて、 安全にキャストできます。

let x: i32 = 5;
let y = x as i64;

transmute

transmuteは、型チェックせず、bit列をほかの型として解釈し直します。 unsafeブロック内でしか使えません。 危険なのでできるだけ使いたくないものです。

たとえば、8bit x 4つの配列をu32に解釈しなおすコードは次のようになります。

use std::mem;

unsafe {
    let a = [0u8, 0u8, 0u8, 0u8];
    let b = mem::transmute::<[u8; 4], u32>(a);
}

transmuteをつかっても、サイズのチェックくらいはしてくれるそうです。 たとえば先ほどの配列au64transmuteすることはできません。

Unsized Types

Rustではほとんどの型はサイズがコンパイル時にわかっています。 例えばi32は32bitという具合です。

しかし、いくつか可変長な型もあります。 そのひとつが[T]で、これはT型がいくつか並んだものです(固定長配列[T; N]とは違います)。 メモリ上の連続した領域に配置されているようです。

安全なプログラムを目指すため、Rustで可変長型をつかう際には制限があります。

  • 可変長型の操作はすべてポインタを通しておこないます。 つまり[T]ではなく&[T]です。
  • Variableや引数には使えません。
  • Structの要素に含めるときは、最後のフィールドでなければなりません。
  • Enumは可変長型を要素にもてません。

?Sized

ジェネリックなstructや関数をつくると、 型パラメータには暗黙的に、Sizedtrait制約がつきます。 そのため、Sizedtraitをもっていない型を受け付けることはできません。

この制約を外すのが?Sizedです。

struct Foo<T: ?Sized> {
    f: T,
}

上のように、?Sizedという(擬似?)traitを要求しておくと、 SizedでないものもTとすることができます。