###### tags: `Rust` `youtube` `pointer` `reference` `struct` `impl` `enum` `env::args()` # Rust language Crush Course 3 # 16. pointer reference Primitive型は値コピー コピー元/先 どちらもオリジナルのデータを持ってるので どちらかを変更しても影響されない ## move value non-Primitive型は挙動が異なる 値コピー(=): コピー元は値を保持しなくなり、コピー先でしか利用できない。moveと呼ばれる 参照コピー(= &): コピー元に値を残したまま、参照して利用できる ```rust= pub fn run(){ // primitive型は値コピーされる let mut arr1 = [1,2,3]; let arr2 = arr1; arr1[0] = 100; println!("arr1: {:?}", &arr1); println!("arr2: {:?}", &arr2); // non-primitive型(vectorなど)は参照コピーされる let mut vec1 = vec![1,2,3]; let vec2 = &vec1; // 以下のコードはエラー // vec2に代入した時点で変更権限を移譲してしまっているため // vec1.push(100); println!("vec1: {:?}", &vec1); println!("vec2: {:?}", vec2); } ``` # 17. Struct, Implement, Trait Rustにはclassがない。 代わりに struct, impl, trait をつかって Struct Orientedなオブジェクト指向で実装する。 3つのキーワード struct : 構造体 impl : 実装 ここでは構造体のメソッドの実装を指す trait : 特徴、特性 ここでは構造体のダックタイピングのためのインターフェイスを指す ## struct 構造体を定義する。 `struct name {member: type, ...}` ## Tuple struct メンバのラベルを定義しないで、タプルで型を指定する 簡易的なstruct リテラル作成時のときもラベルを指定しなくていい ```rust= struct Color{ red: u8, green: u8, blue: u8 } // Tuple Struct struct TupColor(u8, u8, u8); pub fn run(){ let mut c = Color{red: 255,green: 0, blue: 0}; c.red = 200; println!("Color: {} {} {}", c.red, c.green, c.blue); let mut tuplec = TupColor(200, 100, 10); tuplec.0 = 20; println!("Tuple Color: {} {} {}", tuplec.0, tuplec.1, tuplec.2 ) } ``` ## impl implement(実装) ある struct に属するメソッドをメンバとして定義するブロックを作る 以下の例は Person struct に対して - Person.new(): 新しいPerson オブジェクトを作って返す - Person.full_name(): プロパティを結合してフルネームを返す の2種類のメソッドを定義している。 ```rust= struct Person { first_name: String, last_name: String } impl Person { fn new(first: &str, last: &str) -> Person{ Person{first_name: first.to_string(), last_name: last.to_string()} } fn full_name(&self) -> String { format!("{} {}", self.first_name, self.last_name) } } pub fn run(){ let p = Person::new("Hello", "masaharu"); println!("{}", p.full_name()); } ``` ### メソッドの種類の違い ### fn の戻り値 最後の行の ; セミコロンを省略すると、それを戻り値にできる ### &self オブジェクト指向言語のメソッドでは、明示的 or 暗黙的に、第一引数がself or this になっている pythonなどはselfを書く C++, Javaなどは書かない Golang もレシーバーとしてselfと似たような感じで元のオブジェクトを引数に取る。 &self あり: メンバの変数を使うのでオブジェクトが生成されていないと使えない $self なし: オブジェクトが生成されていないくても使える。Staticなメソッド また、呼び出しかたも異なる static : Struct::method() , Struct名のネームスペースに定義された関数というイメージ dynamic: struct_instance.method() , インスタンスのメソッドというイメージ ```rust= pub fn run(){ let p = Person::new("Hello", "masaharu"); println!("{}", p.full_name()); } ``` ### &mut self メソッド内で構造体を変更するときは、引数にmutをつけないといけない ```rust= impl Person { fn set_last_name(&mut self, last: &str) { self.last_name = last.to_string(); } } ``` ### tupleの戻り値 複数の戻り値を返すときはtupleを使うと便利 下の例では、Person struct を Stringタプルに変換する。 また、メンバのリソースを移譲するために、&による参照を使わない ```rust= impl Person { fn to_tuple(self) -> (String, String) { (self.first_name, self.last_name) } } ``` # 18. enum enum = 列挙型 - 上下左右 - 成功、失敗、その他 - 状態(立ち、しゃがみ、歩き) などを決めるのに便利 ## match rust における switch rustでは文(statement)という概念よりも式(expression)を重視し match もまた値を返す。 書式 ```rust= match arg { pattarn1 => expression1, pattarn2 => expression2, pattarn3 => expression3, } ``` ポイント - switch文と異なり、引数に()がいらない - arg == pattarn がtrueなら右のexpressionを評価した値を返す - パターンごとに , カンマで区切る。 ; セミコロンは途中式を区切るのに使う ```rust= enum Movement { // Variant = 変形、異なる値を入れる Up, Down, Left, Right } fn move_avatar(m: Movement) { // Perform action depending on info match m { Movement::Up => println!("Avatar moving Up"), Movement::Down => println!("Avatar moving Down"), Movement::Right => println!("Avatar moving Right"), Movement::Left => println!("Avatar moving Left"), } } pub fn run() { let avatar1 = Movement::Up; let avatar2 = Movement::Down; let avatar3 = Movement::Right; let avatar4 = Movement::Left; move_avatar(avatar1); move_avatar(avatar2); move_avatar(avatar3); move_avatar(avatar4); } ``` # 19. コマンドライン引数 std::env モジュールを使うことで、コマンドライン引数を取得できる。 格納する変数のtypeは Vec<String> 最初の要素は、ファイルまでのパスになる ```rust= use std::env; pub fn run() { let args: Vec<String> = env::args().collect(); println!("args = {:?}", args); let command = args[1].clone(); println!("command: {}", command); } ``` ## String.clone() 通常は、代入式を実行すると所有権が移ってしまうが clone()メソッドを使うと、値コピーをわたせる。 ### コマンドラインツールの作成例 `cli hello` : 挨拶をコンソールに表示 `cli status` : ステータスを表示 `cli <invalid command>` : 不正なコマンドであると通知 ```rust= use std::env; pub fn run() { let args: Vec<String> = env::args().collect(); let command = args[1].clone(); let name = "Brad"; if command == "hello" { println!("Hi {}, how are you?", name); } } ```