Derefとラッパーの使いやすさ
ラッパーを自然な型のように扱えるようにします。
「Derefとラッパーの使いやすさ」はCoddyKit上の無料Learn Rust Codingレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLearn Rust Coding学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Learn Rust Codingコースには全4レッスンが含まれています。
使いやすさの問題
型をnewtypeでラップすると安全性は得られますが、便利さが失われます。突然、あらゆる場所でwrapper.0.method()と書き、メソッドを手作業で再実装しなければならなくなります。
Derefトレイトを使うと、ラッパーが保持する値のように振る舞えるため、この使いやすさの多くを取り戻せます。
Derefの働き
Derefは*valueが生成するものを定義し、.演算子を機能させます。ラッパーにないメソッドを呼び出すと、コンパイラーはデリファレンス先に対してもう一度試します。
この自動的な処理をデリファレンス強制変換と呼びます。
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T { &self.0 }
}Deref強制変換の動作
Derefを実装すると、ラッパー上で内部のメソッドをそのまま呼び出せます。コンパイラーがメソッドを見つけるまで、.deref()を自動的に挿入します。
use std::ops::Deref;
struct Name(String);
impl Deref for Name {
type Target = String;
fn deref(&self) -> &String { &self.0 }
}
fn main() {
let n = Name("rustacean".to_string());
println!("len = {}", n.len()); // String::len via deref
}可変アクセスのためのDerefMut
Derefで得られるのは共有アクセスだけです。ラッパーを通して&mut selfを必要とするメソッドを呼び出すには、内部の値への可変参照を返すDerefMutも実装します。
use std::ops::{Deref, DerefMut};
struct Stack(Vec<i32>);
impl Deref for Stack {
type Target = Vec<i32>;
fn deref(&self) -> &Vec<i32> { &self.0 }
}
impl DerefMut for Stack {
fn deref_mut(&mut self) -> &mut Vec<i32> { &mut self.0 }
}関数境界での強制変換
Deref強制変換は引数にも適用されます。&Nameは&Stringに変換され、さらに&strにも変換されるため、内部参照を必要とする関数にラッパーをそのまま渡せます。
fn greet(who: &str) {
println!("hi {}", who);
}
// greet(&name) works: &Name -> &String -> &str実行可能な強制変換の例
このプログラムでは、文字列スライスが必要な場所にラッパーへの参照を渡します。連鎖したDeref強制変換は、呼び出し箇所で暗黙的に行われます。
use std::ops::Deref;
struct Tag(String);
impl Deref for Tag {
type Target = String;
fn deref(&self) -> &String { &self.0 }
}
fn shout(s: &str) { println!("{}!", s.to_uppercase()); }
fn main() {
let t = Tag("ship".to_string());
shout(&t);
}Derefを乱用しない
Rust APIガイドラインでは、真のスマートポインターではない型にDerefを実装しないよう警告しています。使いすぎると、ラッパーで利用できるメソッドが予測しにくくなり、読者を驚かせます。
newtypeがAPIを制限するために存在する場合、無条件のDerefによって、隠すつもりだったメソッドまで公開される可能性があります。
対象を絞った代替手段としてのAsRef
自動的な強制変換ではなく明示的な変換だけが必要な場合は、AsRefを実装します。呼び出し側が意図的に.as_ref()と書くため、ラッパー独自のAPIを明確に保てます。
struct Path(String);
impl AsRef<str> for Path {
fn as_ref(&self) -> &str { &self.0 }
}選択したメソッドを転送する
制限目的のラッパーでは、公開したいメソッドだけを手作業で記述する方法を優先してください。これにより、Derefですべてを漏らすことなく、APIを完全に制御できます。
struct Counter(Vec<u8>);
impl Counter {
fn len(&self) -> usize { self.0.len() }
fn push(&mut self, b: u8) { self.0.push(b); }
}スマートポインターではDerefを使う
Box、Rc、Arcなどの標準スマートポインターは、すべてDerefを実装しています。そのため、内部のメソッドを透過的に呼び出せます。これが、このトレイトの正当で意図された使い方です。
use std::rc::Rc;
fn main() {
let shared = Rc::new(String::from("data"));
// Deref lets us call String methods directly
println!("len = {}", shared.len());
}使いやすさの方針を選ぶ
目的に応じて選択してください。真のスマートポインターラッパーにはDerefを、明示的な借用変換にはAsRefを、newtypeで内部型のAPIを制限する場合には手動のメソッド転送を使います。
誤った選択をすると、隠しすぎるか、公開しすぎることになります。
理解度チェック
どのラッパーにDerefを実装すべきか考えてみましょう。
まとめ
DerefとDeref強制変換を使うと、.演算子や参照引数が内部の値まで透過するため、ラッパーの使いやすさを取り戻せます。DerefMutを使えば可変アクセスも追加できます。
Derefは真のスマートポインターに限定してください。制限目的のnewtypeでは、明示的な変換にAsRefを使うか、公開するメソッドだけを転送します。
AI チューターと学ぶ Rust — 無料
ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。
- コース
- 39
- レッスン
- 144
よくある質問
「Derefとラッパーの使いやすさ」レッスンは無料ですか?
はい。「Derefとラッパーの使いやすさ」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Learn Rust Codingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Learn Rust Codingコースには全4レッスンが含まれています。
「Derefとラッパーの使いやすさ」で何を学びますか?
ラッパーを自然な型のように扱えるようにします。 ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Learn Rust Codingを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのLearn Rust Codingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「Derefとラッパーの使いやすさ」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このLearn Rust Codingレッスンでコードを書いて実行できますか?
はい。すべてのLearn Rust Codingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- Builderパターン
- Newtypeパターン
- 型状態Builder
- Derefとラッパーの使いやすさ