はじめに
こんにちは。細々とプログラミングをしているsotanengelです。
この記事は以下の記事の連載です。
他の連載記事 (詳細)
- Day 1:型システムを使ってデータ構造を再現しよう
- Day 2:型システムを用いて共通の挙動を表現しよう
- Day 3:OptionとResultに対してはmatchを用いずに変換しよう
- Day 4:標準のErrorを使おう
- Day 5:型変換を理解しよう
- Day 6:newtypeパターンを活用しよう
- Day 7:複雑な型にはビルダを使おう
- Day 8:明示的なループの代わりにイテレータ変換を使用することを検討しよう
- Day 9:標準トレイトに習熟しよう
- Day 10:RIIパターンにはDropトレイトを実装しよう
- Day 11:ジェネリクスとトレイトオブジェクトのトレードオフを理解しよう
- Day 12:デフォルト実装を用いて、実装しなければならないトレイトメソッドを最小限にしよう
- Day 13:Don't panic
- Day 14:リフレクションを避けよう
- Day 15:可視範囲を最小化しよう
また本記事はEffective Rust(David Drysdale (著), 中田 秀基 (翻訳))を参考に作成されております。とてもいい書籍ですので興味を持った方は、ぜひ読んでみてください!
今日の内容
概要
Rustではデフォルトで構造体やメソッドがプライベートになっています。
pubをつけたときにどのような挙動になるか確認しましょう。
pubをつけた時の挙動を確認しよう
問(リンク)
コメントに沿って、適宜pubを付与してください。
コード (詳細)
mod my_mod {
// TODO: この構造体のpublic_valueにだけアクセスできるようにしてください。
struct MyStruct {
public_value: i32,
private_value: i32,
}
// TODO: newとpublic_method、get_private_valueをアクセスできるようにしてください。
impl MyStruct {
fn new(pub_val: i32, priv_val: i32) -> Self {
MyStruct {
public_value: pub_val,
private_value: priv_val,
}
}
fn private_method(&self) {
println!(
"private_method called: public_value={}, private_value={}",
self.public_value, self.private_value
);
}
fn public_method(&self) {
println!("public_method called: public_value={}", self.public_value);
self.private_method();
}
fn get_private_value(&self) -> i32 {
self.private_value
}
}
// TODO: MyEnumにアクセスできるようにしてください。
#[allow(dead_code)]
enum MyEnum {
A,
B,
}
// TODO: MyTraitを実装したメソッド全てにアクセスできるようにしてください。
trait MyTrait {
fn trait_method(&self);
}
pub struct Implementer;
impl MyTrait for Implementer {
fn trait_method(&self) {
println!("Implementer trait_method");
}
}
}
fn main() {
// トレイトをスコープに導入する
use my_mod::MyTrait;
// MyStructの利用
let s = my_mod::MyStruct::new(42, 7);
println!("Public Value: {}", s.public_value);
println!(
"Accessed Private Value through getter: {}",
s.get_private_value()
);
s.public_method();
// MyEnumの利用
let e = my_mod::MyEnum::A;
match e {
my_mod::MyEnum::A => println!("Matched variant A"),
my_mod::MyEnum::B => println!("Matched variant B"),
}
// MyTraitの利用
let impl_obj = my_mod::Implementer;
// traitをスコープに入れているので、trait_methodが使用可能
impl_obj.trait_method();
}
解答(リンク)
コード参照。
書籍の説明としては可視範囲は限りなく少なく
がセオリーとしています。
理由としては、
- 一度範囲をpubにすると元に戻すことが難しいため
- 公開範囲が増えると将来的なメンテナンスの負担が増大するため
とのことでした。
コード (詳細)
mod my_mod {
// パブリックな構造体
pub struct MyStruct {
pub public_value: i32, // パブリックなフィールド
private_value: i32, // 非公開のフィールド
}
impl MyStruct {
// パブリックなコンストラクタ
pub fn new(pub_val: i32, priv_val: i32) -> Self {
MyStruct {
public_value: pub_val,
private_value: priv_val,
}
}
// 非公開のメソッド
fn private_method(&self) {
println!(
"private_method called: public_value={}, private_value={}",
self.public_value, self.private_value
);
}
// パブリックなメソッド
pub fn public_method(&self) {
println!("public_method called: public_value={}", self.public_value);
self.private_method();
}
// 非公開フィールドにアクセスするためのパブリックメソッド
pub fn get_private_value(&self) -> i32 {
self.private_value
}
}
// パブリックなenum
#[allow(dead_code)]
pub enum MyEnum {
A,
B,
}
// パブリックなトレイト
pub trait MyTrait {
fn trait_method(&self);
}
// トレイトを実装する構造体もpub
pub struct Implementer;
impl MyTrait for Implementer {
fn trait_method(&self) {
println!("Implementer trait_method");
}
}
}
fn main() {
// トレイトをスコープに導入する
use my_mod::MyTrait;
// MyStructの利用
let s = my_mod::MyStruct::new(42, 7);
println!("Public Value: {}", s.public_value);
println!(
"Accessed Private Value through getter: {}",
s.get_private_value()
);
s.public_method();
// MyEnumの利用
let e = my_mod::MyEnum::A;
match e {
my_mod::MyEnum::A => println!("Matched variant A"),
my_mod::MyEnum::B => println!("Matched variant B"),
}
// MyTraitの利用
let impl_obj = my_mod::Implementer;
// traitをスコープに入れているので、trait_methodが使用可能
impl_obj.trait_method();
}
さいごに
もしも本リポジトリで不備などあれば、リポジトリのissueやPRなどでご指摘いただければと思います。