rust-systems-programming について
メモリ安全で高性能なソフトウェアを開発する人向けの、Rust システムプログラミングスキルです。所有権、並行処理、非同期処理、unsafe コード、FFI、最適化を扱います。
主な機能
- 01所有権、借用、ライフタイム
- 02スレッド、メッセージパッシング、共有状態
- 03非同期処理とエラー処理
- 04unsafe コードと FFI
- 05メモリ安全性の検査と性能最適化
活用シーン
- 01並行ネットワークサービスの開発
- 02コマンドラインまたは組み込みソフトウェアの構築
- 03FFI による Rust と C の接続
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ manutej/ luxor-claude-marketplace --skill rust-systems-programmingインストール後、Claude Code で/rust-systems-programmingを入力して読み込まれたか確認します。
同梱ファイルEXAMPLES.mdREADME.md
詳細
Rust システムプログラミング
Rust を使って、高性能でメモリー安全なシステムソフトウェアを構築するための包括的なスキルです。システムレベルの開発における所有権、借用、並行処理、非同期プログラミング、unsafe コード、FFI、パフォーマンス最適化を扱います。
このスキルを使う場面
次のような場合にこのスキルを使います。
- ガベージコレクションなしでメモリー安全性が求められるシステムソフトウェアを構築する
- ゼロコスト抽象化を利用して高性能なアプリケーションを開発する
- データ競合を防止しながら、並行または並列プログラムを作成する
- I/O バウンドのワークロード(ウェブサーバー、データベース)向けに async/await アプリケーションを作成する
- 低レベルコード、FFI、ハードウェアインターフェースを扱う
- C/C++ コードをより安全な代替手段に置き換える
- コマンドラインツール、ネットワークサービス、組み込みシステムを構築する
- アプリケーションのパフォーマンスが重要な箇所を最適化する
- コンパイル時にメモリー安全性を保証するライブラリを作成する
- ネイティブに近いパフォーマンスを実現する WebAssembly モジュールを開発する
主な概念
所有権モデル
Rust の所有権システムは、メモリー安全性の保証の基盤です。
所有権のルール:
- Rust の各値には、所有者が必ず 1 つあります
- 所有者がスコープを抜けると、値はドロップされます
- 所有権は新しい所有者に移転(ムーブ)できます
ムーブセマンティクス:
struct MyStruct { s: u32 }
fn main() {
let mut x = MyStruct{ s: 5u32 };
let y = x; // Ownership moved from x to y
// x.s = 6; // ERROR: x is no longer valid
// println!("{}", x.s); // ERROR: cannot use x after move
}
型が Copy を実装していない場合、代入ではコピーではなく所有権が移動します。これにより、二重解放エラーやムーブ後の値の使用によるバグをコンパイル時に防ぎます。
Copy 型の場合:
let x = 5; // i32 implements Copy
let y = x; // x is copied, not moved
println!("{}", x); // OK: x is still valid
借用と参照
借用を使うと、所有権を取得せずにデータへ一時的にアクセスできます。
不変借用:
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // Borrow s1 immutably
println!("The length of '{}' is {}.", s1, len); // s1 still valid
}
fn calculate_length(s: &String) -> usize {
s.len() // Can read but not modify
}
可変借用:
Rust はデータ競合を防ぐため、排他的な可変アクセスを強制します。
fn main() {
let mut value = 3;
let borrow = &mut value; // Mutable borrow
*borrow += 1;
println!("{}", borrow); // 4
// value is accessible again after borrow ends
}
借用のルール:
- 可変参照は 1 つ、または不変参照は任意の数だけ持つことができます
- 参照は常に有効でなければなりません(ダングリングポインターは禁止です)
- 可変借用と不変借用を同時に行うことはできません
よくある借用エラー:
fn main() {
let mut value = 3;
// Create a mutable borrow of `value`.
let borrow = &mut value;
let _sum = value + 1; // ERROR: cannot use `value` because
// it was mutably borrowed
println!("{}", borrow);
}
ライフタイム
ライフタイムにより、参照が常に有効であることが保証されます。
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
'a ライフタイムパラメーターは、返される参照が両方の入力参照が有効である間、有効であることをコンパイラーに伝えます。
共有のための所有権パターン
単一スレッドでの共有所有権に使う Rc(参照カウント):
use std::cell::RefCell;
use std::rc::Rc;
struct MyStruct { s: u32 }
fn main() {
let mut x = Rc::new(RefCell::new(MyStruct{ s: 5u32 }));
let y = x.clone(); // Increment reference count
x.borrow_mut().s = 6; // Interior mutability via RefCell
println!("{}", x.borrow().s);
}
Rc<T> は参照カウントによる共有所有権を提供します。RefCell<T> は内部可変性を可能にし、借用ルールをコンパイル時ではなく実行時に強制します。
スレッドセーフな共有に使う Arc(アトミック参照カウント):
use std::sync::Arc;
use std::thread;
struct FancyNum {
num: u8,
}
fn main() {
let fancy_ref1 = Arc::new(FancyNum { num: 5 });
let fancy_ref2 = fancy_ref1.clone();
let x = thread::spawn(move || {
// `fancy_ref1` can be moved and has a `'static` lifetime
println!("child thread: {}", fancy_ref1.num);
});
x.join().expect("child thread should finish");
println!("main thread: {}", fancy_ref2.num);
}
Arc<T> は Rc<T> のスレッドセーフ版で、参照カウントにアトミック操作を使用します。
ヒープ割り当てのための Box
Box<T>
Box<T> は、T をヒープに割り当てる所有ポインターです。次の用途に役立ちます。
- サイズが既知の再帰型
- スタック上でコピーすべきでない大きな値
- 動的ディスパッチを使うトレイトオブジェクト
並行処理パターン
スレッドとメッセージパッシング
Rust は、所有権システムによってコンパイル時にデータ競合を防ぎます。
use std::thread;
use std::sync::mpsc;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
tx.send("Hello from thread").unwrap();
});
let message = rx.recv().unwrap();
println!("{}", message);
}
Mutex による共有状態
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}
主な並行処理型:
Mutex<T>:共有可変状態のための相互排他ロックRwLock<T>:複数の読み取り、または 1 つの書き込みを許可する読み書きロックArc<T>:スレッドセーフな共有のためのアトミック参照カウントmpsc:マルチプロデューサー、シングルコンシューマーのチャネル
データ競合の防止
ThreadSanitizer の例:
static mut A: usize = 0;
fn main() {
let t = std::thread::spawn(|| {
unsafe { A += 1 };
});
unsafe { A += 1 };
t.join().unwrap();
}
このコードにはデータ競合があります。ThreadSanitizer(RUSTFLAGS=-Zsanitizer=thread で有効化)は、静的な可変データへの並行アクセスを検出します。
WARNING: ThreadSanitizer: data race (pid=10574)
Read of size 8 at 0x5632dfe3d030 by thread T1:
Previous write of size 8 at 0x5632dfe3d030 by main thread:
非同期プログラミング
Async/Await の基本
Rust の非同期プログラミングでは、スレッドをブロックせずに I/O を並行実行できます。
async fn foo(n: usize) {
if n > 0 {
Box::pin(foo(n - 1)).await;
}
}
再帰的な非同期関数では、future のサイズを確定させるために Box::pin() が必要です。
非同期クロージャ
Move セマンティクスを使用する非同期クロージャ:
fn force_fnonce<T: async FnOnce()>(t: T) -> T { t }
let x = String::new();
let c = force_fnonce(async move || {
println!("{x}");
});
AsyncFnOnce に制約されている場合、適切な所有権を確保するため、クロージャは move でキャプチャします。
借用する非同期クロージャ:
let x = &1i32; // Lifetime '1
let c = async move || {
println!("{:?}", *x);
// Even though the closure moves x, we're only capturing *x,
// so the inner coroutine can reborrow the data for its original lifetime.
};
非同期クロージャでの可変借用:
let mut x = 1i32;
let c = async || {
x = 1;
// The parent borrows `x` mutably.
// When we call `c()`, we implicitly autoref for `AsyncFnMut::async_call_mut`.
// The inner coroutine captures with the lifetime of the coroutine-closure.
};
よくある非同期エラー
E0373:有効期間の短い変数をキャプチャする非同期ブロック:
use std::future::Future;
async fn f() {
let v = vec![1, 2, 3i32];
spawn(async { //~ ERROR E0373
println!("{:?}", v) // v might go out of scope before async block runs
});
}
fn spawn<F: Future + Send + 'static>(future: F) {
unimplemented!()
}
解決策:変数を非同期ブロックに move します。
spawn(async move {
println!("{:?}", v)
})
unsafe コードと FFI
unsafe を使用する場面
Rust の unsafe キーワードを使うと、コンパイラーが検証できない操作を実行できます。
- 生ポインターの参照外し
- unsafe 関数またはメソッドの呼び出し
- 可変 static 変数へのアクセスまたは変更
- unsafe トレイトの実装
- 共用体のフィールドへのアクセス
unsafe の参照外しの例:
macro_rules! unsafe_deref {
() => {
*(&() as *const ())
};
}
unsafe を使ったメモリー安全性
unsafe な Sync の実装:
use std::cell::Cell;
struct NotThreadSafe<T> {
value: Cell<T>,
}
unsafe impl<T> Sync for NotThreadSafe<T> {}
static A: NotThreadSafe<usize> = NotThreadSafe { value : Cell::new(1) };
static B: &'static NotThreadSafe<usize> = &A; // ok!
スレッドセーフであることを手動で保証する必要があるため、これは unsafe です。Cell はデフォルトでは Sync ではないため、この実装には慎重な検討が必要です。
FFI(外部関数インターフェース)
Rust から C 関数を呼び出す:
use std::mem;
#[link(name = "foo")]
extern "C" {
fn do_twice(f: unsafe extern "C" fn(i32) -> i32, arg: i32) -> i32;
}
unsafe extern "C" fn add_one(x: i32) -> i32 {
x + 1
}
unsafe extern "C" fn add_two(x: i64) -> i64 {
x + 2
}
fn main() {
let answer = unsafe { do_twice(add_one, 5) };
println!("The answer is: {}", answer);
// Type-mismatched call (unsafe):
println!("With CFI enabled, you should not see the next answer");
let f: unsafe extern "C" fn(i32) -> i32 = unsafe {
mem::transmute::<*const u8, unsafe extern "C" fn(i32) -> i32>(add_two as *const u8)
};
let next_answer = unsafe { do_twice(f, 5) };
println!("The next answer is: {}", next_answer);
}
制御フローの整合性(CFI): CFI を有効にすると、型が一致しない transmute によってプログラムが終了し、制御フローの乗っ取りを防止します。
システムコールのインラインアセンブリー:
static UNMAP_BASE: usize;
const MEM_RELEASE: usize;
static VirtualFree: usize;
const OffPtr: usize;
const OffFn: usize;
core::arch::asm!("
push {free_type}
push {free_size}
push {base}
mov eax, fs:[30h]
mov eax, [eax+8h]
add eax, {off_fn}
mov [eax-{off_fn}+{off_ptr}], eax
push eax
jmp {virtual_free}
",
off_ptr = const OffPtr,
off_fn = const OffFn,
free_size = const 0,
free_type = const MEM_RELEASE,
virtual_free = sym VirtualFree,
base = sym UNMAP_BASE,
options(noreturn),
);
これは、Windows のメモリー解放にインラインアセンブリーを使用して、システムコールを直接実行する例です。
エラー処理
Result 型と Option 型
Rust では、エラー処理に Result<T, E> と Option<T> を使用します。
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err("Division by zero".to_string())
} else {
Ok(a / b)
}
}
fn main() {
match divide(10, 2) {
Ok(result) => println!("Result: {}", result),
Err(e) => println!("Error: {}", e),
}
}
? 演算子
fn process_file(path: &str) -> Result<String, std::io::Error> {
let content = std::fs::read_to_string(path)?;
Ok(content.to_uppercase())
}
? 演算子は、例外と同様にエラーを呼び出しスタック上位へ伝播しますが、型シグネチャに明示されます。
カスタムエラー型
use std::fmt;
#[derive(Debug)]
enum AppError {
IoError(std::io::Error),
ParseError(std::num::ParseIntError),
Custom(String),
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AppError::IoError(e) => write!(f, "IO error: {}", e),
AppError::ParseError(e) => write!(f, "Parse error: {}", e),
AppError::Custom(msg) => write!(f, "Error: {}", msg),
}
}
}
impl std::error::Error for AppError {}
メモリー安全性とサニタイザー
AddressSanitizer
スタックバッファーオーバーフローの検出:
fn main() {
let xs = [0, 1, 2, 3];
let _y = unsafe { *xs.as_ptr().offset(4) };
}
AddressSanitizer を有効にしてビルドします。
$ export RUSTFLAGS=-Zsanitizer=address RUSTDOCFLAGS=-Zsanitizer=address
$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu
出力:
==37882==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe400e6250
READ of size 4 at 0x7ffe400e6250 thread T0
#0 0x5609a841fb1f in example::main::h628ffc6626ed85b2 /.../src/main.rs:3:23
Address 0x7ffe400e6250 is located in stack of thread T0 at offset 48 in frame
This frame has 1 object(s):
[32, 48) 'xs' (line 2) <== Memory access at offset 48 overflows this variable
ヒープバッファーオーバーフローの検出:
fn main() {
let xs = vec![0, 1, 2, 3];
let _y = unsafe { *xs.as_ptr().offset(4) };
}
スコープ終了後の使用の検出:
static mut P: *mut usize = std::ptr::null_mut();
fn main() {
unsafe {
{
let mut x = 0;
P = &mut x;
}
std::ptr::write_volatile(P, 123); // P points to dropped variable
}
}
AddressSanitizer の出力:
==39249==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffc7ed3e1a0
WRITE of size 8 at 0x7ffc7ed3e1a0 thread T0
パフォーマンス最適化
ゼロコスト抽象化
Rust では、実行時のオーバーヘッドなしで高水準の抽象化を利用できます。
// Iterator chains are optimized to simple loops
let sum: i32 = (1..100)
.filter(|x| x % 2 == 0)
.map(|x| x * x)
.sum();
コンパイラーはこれを、手動で反復処理を行う場合と同等の効率的なループに最適化します。
インライン化と単相化
ジェネリック関数は、具体的な型ごとに単相化(特殊化)されます。
#[inline]
fn add<T: std::ops::Add<Output = T>>(a: T, b: T) -> T {
a + b
}
let x = add(5, 3); // Specialized for i32
let y = add(5.0, 3.0); // Specialized for f64
スマートポインターのオーバーヘッド
スマートポインターによってコストは異なります。
Box<T>:ヒープの単一割り当てで、オーバーヘッドはありませんRc<T>:参照カウントにより、クローンやドロップごとにわずかなオーバーヘッドが発生しますArc<T>:アトミック参照カウントにより、スレッドセーフ性のためのオーバーヘッドが大きくなりますMutex<T>:ロック取得のオーバーヘッドが発生しますRefCell<T>:実行時の借用チェックによるオーバーヘッドが発生します
割り当ての回避
// Bad: Allocates a new String
fn greet_bad(name: &str) -> String {
format!("Hello, {}", name)
}
// Good: Returns a reference, no allocation
fn greet_good(name: &str) -> impl std::fmt::Display + '_ {
format_args!("Hello, {}", name)
}
よくあるパターンとイディオム
ビルダーパターン
struct Config {
host: String,
port: u16,
timeout: u64,
}
impl Config {
fn builder() -> ConfigBuilder {
ConfigBuilder::default()
}
}
#[derive(Default)]
struct ConfigBuilder {
host: Option<String>,
port: Option<u16>,
timeout: Option<u64>,
}
impl ConfigBuilder {
fn host(mut self, host: impl Into<String>) -> Self {
self.host = Some(host.into());
self
}
fn port(mut self, port: u16) -> Self {
self.port = Some(port);
self
}
fn build(self) -> Config {
Config {
host: self.host.unwrap_or_else(|| "localhost".to_string()),
port: self.port.unwrap_or(8080),
timeout: self.timeout.unwrap_or(30),
}
}
}
Newtype パターン
struct UserId(u64);
struct PostId(u64);
fn get_user(id: UserId) -> User { /* ... */ }
// This won't compile: type safety!
// get_user(PostId(42));
RAII(リソース取得は初期化)
struct FileGuard {
file: std::fs::File,
}
impl FileGuard {
fn new(path: &str) -> std::io::Result<Self> {
Ok(FileGuard {
file: std::fs::File::create(path)?,
})
}
}
impl Drop for FileGuard {
fn drop(&mut self) {
println!("File closed automatically");
}
}
クロージャのパターンとエラー
E0500:クロージャの借用競合
問題:
fn you_know_nothing(jon_snow: &mut i32) {
let nights_watch = &jon_snow;
let starks = || {
*jon_snow = 3; // error: closure requires unique access to `jon_snow`
// but it is already borrowed
};
println!("{}", nights_watch);
}
解決策:
fn you_know_nothing(jon_snow: &mut i32) {
let nights_watch = &jon_snow;
println!("{}", nights_watch); // Use the borrow first
// Borrow ends here (non-lexical lifetimes)
let starks = || {
*jon_snow = 3; // Now OK
};
}
E0502:可変借用と不変借用
問題:
fn bar(x: &mut i32) {}
fn foo(a: &mut i32) {
let y = &a;
bar(a); // Error: cannot borrow as mutable while borrowed as immutable
println!("{}", y);
}
解決策:
fn bar(x: &mut i32) {}
fn foo(a: &mut i32) {
bar(a); // Mutable borrow first
let y = &a; // Immutable borrow after mutable borrow ends
println!("{}", y);
}
E0505:借用中の値の移動
問題:
struct Value {}
fn borrow(val: &Value) {}
fn eat(val: Value) {}
fn main() {
let x = Value{};
let _ref_to_val: &Value = &x;
eat(x); // Error: cannot move x while borrowed
borrow(_ref_to_val);
}
E0524:クロージャでの並行する可変借用
問題:
fn set(x: &mut isize) {
*x += 4;
}
fn dragoooon(x: &mut isize) {
let mut c1 = || set(x);
let mut c2 = || set(x); // error: two closures trying to borrow mutably
c2();
c1();
}
解決策 1:逐次実行(非字句的ライフタイム)
fn set(x: &mut isize) {
*x += 4;
}
fn dragoooon(x: &mut isize) {
{
let mut c1 = || set(&mut *x);
c1();
} // `c1` has been dropped here so we're free to use `x` again!
let mut c2 = || set(&mut *x);
c2();
}
解決策 2:共有の可変アクセスに Rc と RefCell を使用
use std::rc::Rc;
use std::cell::RefCell;
fn set(x: &mut isize) {
*x += 4;
}
fn dragoooon(x: &mut isize) {
let x = Rc::new(RefCell::new(x));
let y = Rc::clone(&x);
let mut c1 = || { let mut x2 = x.borrow_mut(); set(&mut x2); };
let mut c2 = || { let mut x2 = y.borrow_mut(); set(&mut x2); };
c2();
c1();
}
E0507:借用中の内容の移動
問題:
use std::cell::RefCell;
struct TheDarkKnight;
impl TheDarkKnight {
fn nothing_is_true(self) {}
}
fn main() {
let x = RefCell::new(TheDarkKnight);
x.borrow().nothing_is_true(); // Error: cannot move out of borrowed content
}
解決策 1:参照を使用する
impl TheDarkKnight {
fn nothing_is_true(&self) {} // Change to &self
}
fn main() {
let x = RefCell::new(TheDarkKnight);
x.borrow().nothing_is_true(); // OK
}
解決策 2:所有権を取り戻す
fn main() {
let x = RefCell::new(TheDarkKnight);
let x = x.into_inner(); // Reclaim ownership
x.nothing_is_true(); // OK
}
本番環境向けのパターン
構造化ログ
use tracing::{info, warn, error, instrument};
#[instrument]
async fn process_request(id: u64) -> Result<(), Error> {
info!(request_id = id, "Processing request");
match do_work(id).await {
Ok(_) => {
info!("Request completed successfully");
Ok(())
}
Err(e) => {
error!(error = ?e, "Request failed");
Err(e)
}
}
}
構成管理
use serde::Deserialize;
#[derive(Deserialize)]
struct AppConfig {
database_url: String,
server_port: u16,
log_level: String,
}
fn load_config() -> Result<AppConfig, config::ConfigError> {
config::Config::builder()
.add_source(config::File::with_name("config"))
.add_source(config::Environment::with_prefix("APP"))
.build()?
.try_deserialize()
}
グレースフルシャットダウン
use tokio::signal;
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
println!("Shutdown signal received, starting graceful shutdown");
}
コネクションプーリング
use deadpool_postgres::{Config, Pool, Runtime};
use tokio_postgres::NoTls;
async fn create_pool() -> Pool {
let mut cfg = Config::new();
cfg.host = Some("localhost".to_string());
cfg.dbname = Some("mydb".to_string());
cfg.create_pool(Some(Runtime::Tokio1), NoTls)
.expect("Failed to create pool")
}
async fn query_user(pool: &Pool, id: i64) -> Result<User, Error> {
let client = pool.get().await?;
let row = client
.query_one("SELECT * FROM users WHERE id = $1", &[&id])
.await?;
Ok(User::from_row(row))
}
ベストプラクティス
API 設計
- デフォルトで参照を使用する:所有権が必要な場合を除き、
Tではなく&Tを受け取ります - 所有型を返す:API を簡潔にするため、
&strではなくStringを返します - 標準トレイトを実装する:
Debug、Clone、PartialEq、Send、Sync - 柔軟性のために
Into<T>を使用する:fn set_name(&mut self, name: impl Into<String>) - 型システムを活用する:正確性を確保するために newtype、enum、Result を使用します
テストパターン
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_addition() {
assert_eq!(add(2, 2), 4);
}
#[test]
#[should_panic(expected = "division by zero")]
fn test_divide_by_zero() {
divide(10, 0);
}
#[tokio::test]
async fn test_async_function() {
let result = fetch_data().await;
assert!(result.is_ok());
}
}
ドキュメント
/// Calculates the sum of two numbers.
///
/// # Examples
///
/// ```
/// let result = add(2, 2);
/// assert_eq!(result, 4);
/// ```
///
/// # Panics
///
/// This function will panic if the result overflows.
pub fn add(a: i32, b: i32) -> i32 {
a.checked_add(b).expect("overflow in add")
}
依存関係の管理
[dependencies]
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
tracing = "0.1"
[dev-dependencies]
criterion = "0.5"
[profile.release]
lto = true
codegen-units = 1
トラブルシューティング
よくある借用チェッカーの問題
問題:「不変で借用されているため、可変として借用できません」 解決策:可変借用と不変借用がスコープ内で重複しないようにします
問題:「借用されたコンテンツからムーブできません」
解決策:値をクローンするか、参照を取るか、Rc<RefCell<T>> を使用します
問題:「参照のライフタイムに関する問題」 解決策:明示的なライフタイム注釈を追加するか、複雑なライフタイムを避けるようにコードを再構成します
パフォーマンスの問題
問題:コンパイル時間が長い
解決策:インクリメンタルコンパイルを使用し、ジェネリックのインスタンス化を減らし、cargo check を使用します
問題:実行時のパフォーマンスが予想より遅い
解決策:perf でプロファイリングし、LTO を有効にし、不要な割り当てやクローンがないか確認します
問題:メモリ使用量が多い 解決策:クローンの代わりに参照を使用し、データのストリーミングを検討し、Valgrind でプロファイリングします
並行処理の問題
問題:複数のミューテックスによるデッドロック
解決策:常に同じ順序でロックを取得し、タイムアウト付きの try_lock を使用します
問題:unsafe コード内のデータ競合 解決策:ThreadSanitizer で実行し、unsafe ブロックを慎重に確認します
問題:非同期タスクが進行しない
解決策:非同期コード内のブロッキング操作を確認し、CPU バウンドの処理には spawn_blocking を使用します
クイックリファレンス
スマートポインター早見表
Box<T> - Heap allocation, single owner
Rc<T> - Reference counted, single-threaded
Arc<T> - Atomic reference counted, thread-safe
Mutex<T> - Mutual exclusion lock
RwLock<T> - Reader-writer lock
RefCell<T> - Runtime borrow checking
Cell<T> - Interior mutability for Copy types
よく使われるトレイトの実装
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct MyStruct {
field: String,
}
impl Default for MyStruct {
fn default() -> Self {
Self {
field: String::new(),
}
}
}
impl std::fmt::Display for MyStruct {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "MyStruct({})", self.field)
}
}
非同期ランタイムの比較
Tokio: Full-featured, most popular, excellent ecosystem
async-std: Mirrors std library API, simpler for beginners
smol: Lightweight, minimal dependencies
リソース
- The Rust Book:https://doc.rust-lang.org/book/
- Rust by Example:https://doc.rust-lang.org/rust-by-example/
- The Rustonomicon(unsafe Rust):https://doc.rust-lang.org/nomicon/
- Async Book:https://rust-lang.github.io/async-book/
- Rust Performance Book:https://nnethercote.github.io/perf-book/
- Crate ドキュメント:https://docs.rs/
スキルのバージョン:1.0.0 最終更新日:2025年10月 スキルのカテゴリ:システムプログラミング、パフォーマンス、メモリ安全性 Context7 との統合:Rust のドキュメントとエラーコードの例