注意: 最新版のドキュメントをご覧ください。この第1版ドキュメントは古くなっており、最新情報が反映されていません。リンク先のドキュメントが現在の Rust の最新のドキュメントです。
メモリ割り当てが常に簡単に出来るとは限りません。ほとんどの場合、Rustが既定の方法でメモリ割り当てを行いますが、割り当て方法をカスタマイズする必要が出てくる場合があります。現在、コンパイラと標準ライブラリはコンパイル時に既定のグローバルアロケータを切り替えることが出来ます。詳細は RFC 1183 に書かれていますが、ここではどのように独自のアロケータを作成するか順を追って説明します。
現在コンパイラは alloc_system
と alloc_jemalloc
(jemallocのないターゲットもあります)という2つの既定のアロケータを提供しています。これらのアロケータは単に普通のRustのクレートで、メモリの割り当てと解放の手続きを実装しています。標準ライブラリはどちらか一方を前提としてコンパイルされているわけではありません。コンパイラは生成する成果物の種類に応じてどちらのアロケータを使用するかをコンパイル時に決定します。
バイナリを生成する場合、既定では(もし可能なら) alloc_jemalloc
を使用します。この場合、コンパイラは最後のリンクにまで影響力を持っているという意味で、「全世界を支配」しています。従ってアロケータの選択はコンパイラに委ねることができます。
一方、動的あるいは静的ライブラリの場合、既定では alloc_system
を使用します。他のアプリケーションや他の環境など、使用するアロケータの決定権がない世界において、Rustは「お客」に過ぎません。そのため、メモリの割り当てと解放を行うには、標準API(例えば malloc
と free
)に頼ることになります。
コンパイラによる既定の選択はほとんどの場合うまく動きますが、しばしば多少の調整が必要になることがあります。コンパイラのアロケータ選択を上書きするには、単に希望のアロケータとリンクするだけです。
#![feature(alloc_system)] extern crate alloc_system; fn main() { // let a = Box::new(4); // allocates from the system allocator let a = Box::new(4); // システムアロケータからのメモリ割り当て println!("{}", a); }#![feature(alloc_system)] extern crate alloc_system; fn main() { let a = Box::new(4); // システムアロケータからのメモリ割り当て println!("{}", a); }
この例で生成されるバイナリは既定のjemallocとリンクする代わりに、システムアロケータを使います。逆に既定でjemallocを使う動的ライブラリを生成するには次のようにします。
#![feature(alloc_jemalloc)] #![crate_type = "dylib"] extern crate alloc_jemalloc; pub fn foo() { // let a = Box::new(4); // allocates from jemalloc let a = Box::new(4); // jemallocからのメモリ割り当て println!("{}", a); } fn main() {}#![feature(alloc_jemalloc)] #![crate_type = "dylib"] extern crate alloc_jemalloc; pub fn foo() { let a = Box::new(4); // jemallocからのメモリ割り当て println!("{}", a); }
時々jemallocとシステムアロケータの選択では足りず、全く新しいカスタムアロケータが必要になることがあります。この場合、アロケータAPI(例えば alloc_system
や alloc_jemalloc
と同様のもの)を実装した独自のクレートを書くことになります。例として、 alloc_system
の簡素な注釈付きバージョンを見てみましょう。
// コンパイラがリンク時に他のアロケータ(例えばjemalloc)とリンクしてしまうことを防ぐため、 // このクレートがアロケータであることを示す必要があります。 #![feature(allocator)] #![allocator] // 循環依存を避けるため、アロケータはアロケータを使う標準ライブラリに依存してはいけません。 // しかしlibcoreについては全ての機能を使用できます。 #![no_std] // カスタムアロケータに固有の名前を付けてください。 #![crate_name = "my_allocator"] #![crate_type = "rlib"] // この独自アロケータはFFIバインディングのためにRustコンパイラのソースコードに // 同梱されているlibcクレートを使います。 // 注記: 現在の外部libc(crate.ioのもの)は標準ライブラリとリンクしているため使用できません // ( `#![no_std]` がまだstableではないためです)。そのため特別に同梱版のlibcが必要になります。 #![feature(libc)] extern crate libc; // 今のところ、カスタムアロケータには以下の5つのメモリ割り当て関数が必要です。 // コンパイラは現時点では関数のシグネチャとシンボル名の型検査を行いません。 // しかし、将来的には以下の型に一致する必要があります。 // // 注記: 標準の `malloc` と `realloc` 関数へはアラインメントについての情報を // 渡すことができません。そのためアラインメントを考慮するためにはこの実装は // 改良の必要があります。 #[no_mangle] pub extern fn __rust_allocate(size: usize, _align: usize) -> *mut u8 { unsafe { libc::malloc(size as libc::size_t) as *mut u8 } } #[no_mangle] pub extern fn __rust_deallocate(ptr: *mut u8, _old_size: usize, _align: usize) { unsafe { libc::free(ptr as *mut libc::c_void) } } #[no_mangle] pub extern fn __rust_reallocate(ptr: *mut u8, _old_size: usize, size: usize, _align: usize) -> *mut u8 { unsafe { libc::realloc(ptr as *mut libc::c_void, size as libc::size_t) as *mut u8 } } #[no_mangle] pub extern fn __rust_reallocate_inplace(_ptr: *mut u8, old_size: usize, _size: usize, _align: usize) -> usize { old_size // このAPIはlibcではサポートされていません。 } #[no_mangle] pub extern fn __rust_usable_size(size: usize, _align: usize) -> usize { size }
このクレートをコンパイルすると、次のように使えるようになります。
extern crate my_allocator; fn main() { // let a = Box::new(8); // allocates memory via our custom allocator crate let a = Box::new(8); // カスタムアロケータによるメモリ割り当て println!("{}", a); }extern crate my_allocator; fn main() { let a = Box::new(8); // カスタムアロケータによるメモリ割り当て println!("{}", a); }
カスタムアロケータを使用する場合、コンパイルエラーの原因となりうるいくつかの制限があります。
#![needs_allocator]
でタグ付けされます(例えば現時点での liballoc
クレート)。また、 #[allocator]
がついたクレートはアロケータを使うクレートに直接的にも間接的にも依存することが出来ません(例えば、循環依存は許されていません)。このためアロケータは原則としてlibcoreにしか依存しないようにする必要があります。