Rustでオブジェクト指向的に関数を実装する方法を自分が読みやすいようににかんたんにまとめた。
間違っている可能性が大きいので指摘をお願いします。
2022/06/07:間違っている部分が有りましたので修正しました。
オブジェクト指向経験者のためのRust入門のimplまでを参考にして必要以上に細かく書いている感じです。
Rustで"this is an associated function, not a method"のエラーが出たのでメソッドと関連関数について調べてみたも参考にしています。
Struct = Class のような雰囲気
pub struct Class1 { //パブリックなstructを作成
num: i32,
is_true: bool
}
クラスに定義される属性(変数)はstruct
ブロックの中で書くことができる
関連関数を書くときはimpl
impl Class1 {
pub fn new(is_true: bool, num: i32) -> Self {
println!("You will be able to use Class1 object I will return.");
return Class1 { //ここのreturnは省略可,Class1はSelfとすることも可能
num: num,
is_true: is_true,
}
}
pub fn func1(&self, num: i32){
println!("You called this function with number: {}", num)
}
}
クラスを定義したとき、そのクラスのオブジェクトを作るためにnew
関数が必要になることがあるだろう。
このnew
関数はprintln()
メソッドで説明を出力した後に渡された2つの情報num
とis_true
の2つを使ってClass1を返す。
このようにimpl
キーワードを使用してオブジェクトへ実装した関数のことを関連関数という。
-> Class1のnew関数関係を一文目から説明していく。
1. impl Class1
でClass1に関数を実装できる。
impl
はおそらくimplementationの略であり、実装するということである。
impl
の後に関数を実装したい構造体(struct)を書く。
2. 実装したい関数を書く。
標準はスコープがprivateなため外部から呼び出すことが出来ないので、pub
キーワードを冒頭につけ外部から呼び出せるようにする。new
関数なので呼び出せないとまずいだろう。
このとき、返り値がユニット型(()
で表される空のタプル-> 要するに何も返すものがない)でないので、返す値の型を宣言する必要がある。
2022/06/07,訂正
コメント欄で指摘を頂きました。C++もどきを書いてしまっていました。
間違った表記を参考にしてしまった方がいらっしゃいましたら申し訳ないです。指摘で書いていただいたコードを使用させていただきます。
@yaito3014さん、ありがとうございました。
C++では関数名(func1
など)の前に返り値を書いていた(次のように)が、
class ClassName {
public:
int func1(int num) {
printf("You called this function with number: %d\n", num);
return num;
}
};
Rustでは矢印表記の後に明記する必要がある。
pub fn func1(&self, num: i32) -> i32 { //[-> i32]の表記で返り値の型がi32であることを宣言している
println!("You called this function with number: {}", num);
return num; //returnは省略可
}
3. println()
マクロでnew
関数実行時にコンソールへ文字出力をする
4. return Class1
で返すClass1オブジェクトを作っている。Class1
クラスの関数でClass1
オブジェクトを返すので、return Self
と書くこともできる。
-> オブジェクトのメソッドと関連関数
まず、関連関数とはimpl
キーワードでオブジェクトに対して実装された関数のことであった。
ここでこの2つの関連関数の違いを見てみよう。
pub fn func1(&self, num: i32) -> i32 {
println!("You called this function with number: {}", num);
return num; //returnは省略可
}
pub fn func2(num: i32) -> i32 {
println!("You called this function with number: {}", num);
return num; //returnは省略可
}
これら2つの関数行う処理で違う部分は、第一引数に&self
を取るか否かである。
ここで引数に&self
を取る関連関数func1を、メソッドと呼ぶ。
ここでメソッドとただの関連関数の違いは、インスタンスから呼び出すことができるか否かだ。
それぞれインスタンスから呼び出してみると、
println!("{}",cls1_instance.func1(32));
println!("{}",cls1_instance.func2(32));
26 | println!("{}",cls1test.func2(32));
| ^^^^^ this is an associated function, not a method
|
= note: found the following associated functions; to be used as methods, functions must have a `self` parameter
note: the candidate is defined in an impl for the type `Class1`
--> src/main.rs:18:5
2つ目の関数でエラーが発生し、エラーメッセージからfunc2
はメソッドでないため、インスタンスから呼び出すことが出来ないから発生したエラーだとわかる。
ただの関連関数はクラス名::関数名()
のようにして呼び出すことはできるが、インスタンス.関数名()
と呼び出すことは関連関数では出来ず、メソッドとして定義する必要があることに注意する必要がある。
メソッドの第一引数&self
メソッドを作るためには第一引数を&self
としなければならなかったが、この&self
はメソッドが呼び出されたインスタンスへの参照である。
このことを確かめるため、次のコードを実行してみる。
pub struct Class1 { //パブリックなstructを作成
num: i32,
is_true: bool
}
impl Class1 {
pub fn new(is_true: bool, num: i32) -> Self {
println!("You will be able to use Class1 object I will return.");
return Self {
num: num,
is_true: is_true,
}
}
pub fn func1(&self, num_arg: i32){ //返り値はユニット型になるので->などは不要
println!("This function was called by you with number: {}.\nAnd, The num attribute of the instance used to call this function is {}", num_arg, self.num);
}
}
fn main(){
let cls1test = Class1::new(true, 32); //第2引数で32を指定しているのでnum属性は32となる。
cls1test.func1(50); //呼び出す際の引数では50を指定する。
}
You will be able to use Class1 object I will return.
This function was called by you with number: 50.
And, The num attribute of the instance used to call this function is 32
出力結果とコードからメソッドが呼び出されたインスタンス(cls1test
)の参照が&selfであることがわかる。