Rust 人門 |
ここではRustの新奇性のみにしぼって解説する。 実際的なこと(インストール方法とか)は説明しない。
fn main() { let x = 2; // これは let y = 3; // コメント println!("x+y={}", x+y); }
!
がつくものはマクロ (hygienic) である。
struct Point { x: i32, y: i32, } fn main() { let p = Point { x:2, y:3 }; println!("p={},{}", p.x, p.y); }
i32
= int
p
はスタック上に存在。
fn main() {
let q = Box::new(Point { x:2, y:3 });
println!("q={},{}", (*q).x, (*q).y);
println!("q={},{}", q.x, q.y); // OK
}
Box::new()
= 基本的に malloc。
*
で解決する。
main() { file f("hello.txt"); // ファイルが開かれる。 string s(f.read()); // 文字列が初期化される。 cout << s << endl; // f, s が自動的に解放される。 }
fn main() { { let q = Box::new(Point { x:2, y:3 }); println!("q={},{}", q.x, q.y); // q は勝手に解放される。 } println!("q={},{}", q.x, q.y); // ERROR }
fn main()
{
let s = Some(Point { x:2, y:3 });
// または s = None;
match s {
Some(p) => println!("p={},{}", p.x, p.y),
None => println!("p=null"),
}
}
nil
かどうか」しか判定できない。
let s: Point? if let p = s { print("p=\(p.x),\(p.y)") } else { print("p=null") }
show(Point* p) {
printf("p=%d,%d\n", p->x, p->y);
delete p;
}
main() {
Point* p = new Point(2, 3);
show(p);
delete p; // Double free
}
fn show(p: Box<Point>) { println!("p={},{}", p.x, p.y); // RAIIにより勝手に解放される。 } fn main() { let p = Box::new(Point { x:2, y:3 }); show(p); // pの所有権がshowに移る。 }
free
する責任。
fn show(p: Box<Point>) { println!("p={},{}", p.x, p.y); } fn main() { let p = Box::new(Point { x:2, y:3 }); show(p); // この時点で、pはすでにmainのものではない。 println!("area={}", p.x * p.y); // ERROR }
fn alloc() -> Box<Point> { let p = Box::new(Point { x:2, y:3 }); // 所有権を返す。 return p; } fn main() { let p = alloc(); // 所有権を得る。 println!("p={},{}", p.x, p.y); // RAIIにより解放。 }
fn show(p: &Box<Point>) { // pは借り物。 println!("p={},{}", p.x, p.y); } fn main() { let p = Box::new(Point { x:2, y:3 }); show(p); // 貸しただけ。 println!("area={}", p.x * p.y); // 所有者はまだ俺だから解放しなきゃ。 }
&
がついた型は「借り物 (borrow)」であることを表す。
fn area(p: Box<Point>) -> i32 { // 本物 return p.x * p.y; } fn show(p: &Box<Point>) { // 借り物 println!("p={},{}", p.x, p.y); // 借り物を本物として渡そうとすると println!("area={}", area(p)); // ERROR } fn main() { let p = Box::new(Point { x:2, y:3 }); show(p); }
fn main() { let p = Point { x:2, y :3 }; println!("p={},{}", p.x, p.y); let q = p; println!("area={}", q.x * q.y); // OK println!("area={}", p.x * p.y); // ERROR }
class Foo {
Point* p;
public:
Foo(Point* p) {
this->p = p;
}
};
main() {
Point* p = new Point(2,3);
Foo* foo = new Foo(p);
delete p;
// foo->p は Dangling
}
struct Foo { pt: Box<Point>, // 所有権をもつ。 } fn main() { let p = Box::new(Point { x:2, y:3 }); // pの所有権は foo内に移動する。 let foo = Foo { pt:p }; // もう p は俺の物じゃない。 println!("p={},{}", p.x, p.y); // ERROR // fooが解放されると p も解放される。 }
struct Foo { pt: &Box<Point>, // 借り物 } fn alloc(&Box<Point>) -> Foo { return Foo { pt:p }; // 借り物が入った本物 } fn main() { let p = Box::new(Point { x:2, y:3 }); let foo = alloc(&p); println!("p={},{}", p.x, p.y); // OK...のはず } // 実際にはコンパイル不可
struct Foo {
pt: &Box<Point>, // 借り物
}
Foo
型は限られた期間だけ有効。
Foo
はパラメータ化された型なのである。
struct Foo<'a> { // pt は、少なくとも Foo本体と期間は生きる。 pt: &'a Box<Point>, } // 「与えられた p と同じ期間は生きる Foo」を返す。 fn alloc<'a>(p: &'a Box<Point>) -> Foo<'a> { return Foo { pt:p }; }
'a
, 'b
のように表す。
(型をさらに限定するという意味で <
...>
が使われている。)
&
)」に対してのみ。
fn main() {
let p = Box::new(Point { x:2, y:3 });
let foo = alloc(&p); // p と同じ生存期間をもつ。
println!("p={},{}", p.x, p.y);
}
struct Foo<'a> {
pt: &'a Box<Point>,
}
// 簡略版。
fn alloc(p: &Box<Point>) -> Foo {
return Foo { pt:p };
}
Foo
構造体の定義では省略不可)
// 「与えられた p と同じ期間は生きる Foo」を返す。 // でも q は関係ないので別の生存期間。 fn alloc<'a>(p: &'a Box<Point>, q: &'b Box<Point>) -> Foo<'a> { println!("q={},{}", q.x, q.y); return Foo { pt:p }; }
channel
… メッセージ送受信に使う。
一旦メッセージを送ったら、そのデータの所有権は移動する。
ARC
… 借り物を共有するときに使われる
排他制御 + 参照カウンタつきスマートポインタ。
RC
… 所有権を移動するときに使われる
参照カウンタつきスマートポインタ。
unsafe { ... }
で囲んだ部分は、所有権・生存期間の規則を無視できる。