2017年6月24日土曜日

The Rust Programming Language 2nd 6日目 struct

![](https://doc.rust-lang.org/book/second-edition/]
Apache License Version 2.0

Chapter 4. Understanding Ownership

4.3 Slices

ownershipを持たないデータ型にsliceがある.sliceによってheapデータの一部分のreferenceを得られる.

String Slices

string sliceStringの一部へのreferenceであって,

let s = String::from("hello world!");
let hello = &s[0..5];
let world = &s[6..11];

という形をしている.&s[i, j]sの左からi番目からj-1番目までへのreferenceで,sのheapのi番目へのポインタと長さをスタックにしたデータである.

sの最後まで含んだsliceを作るときは,&s[i..]と,終端を省略できる.sをまるごとsliceにするときは,単に&s[..]と書く.String型のsliceを&strと書く.
&Stringを引数として,それに初めて空白が現れるまでの文字列のsliceを返す関数first_word()を実装する.

src/main.rs

fn first_word(s: &String) -> &str {
  let bytes = s.as_bytes();

  for (i, &iter) in bytes.iter().enumerate() {
    if item == b' ' {
      return &s[0..i];
    }
  }
  &s[..]
}

String Literals Are Slices

let s = "Hello, world!";
としたとき,sはどこかにあるimmutableな“`”Hello, world!”へのsliceである.

String Slices as Parameters

fn first_word(s: &String) -> &str {
はStringのreferenceしか引数に取れないが,文字列リテラルがStringのsliceであることを考えれば,同時に文字列リテラルを引数に取れるようにfirst_wordを改良できる.

src/main.rs

fn first_word(s: &str) -> &str {
  let bytes = s.as_bytes();

  for (i, &item) in bytes.iter().enumerate() {
    if item == b' ' {
      return &s[0..i];
    }
  }
  &s[..]
}

fn main() {
  let my_string = String::from("hello world!");
  let word_1 = first_word(&my_string[..]);            // my_string[..]はslice

  let my_string_literal = "hello, world";   
  let word_2 = first_word(&my_string_literal[..]);    // my_string_literal[..]はSlice
  let word_3 = first_word(&my_string_literal);        // my_string_literalはすでにslice

  println!("first_words is: {}, {}, {}", word_1, word_2, word_3);
}

shell

    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/ownership`
first_words is: hello, hello,, hello,

Other Slices

Array型にもsliceを考えられる.

let a = [1, 2, 3, 4, 5];
let slice = &a[1..3];

この場合sliceは“`&[i32]”’型となる.

Chapter 5. Structs

structはプログラマが定義できるデータ型で,オブジェクト指向言語におけるクラスのような概念である.タプルと同様に異なった型の変数をその中に持つことが出来るが,それぞれの変数に名前をつけることが出来るから,変数の個数を増減させるとき,いちいち変数のインデックスを考えなくて良い.

structを定義するには,struct 名前 { 名前1: 型1, ...}と書く.例えば

struct User{
  username: String,   // &strにしなかった理由は次節
  email: String,
  sign_in_count: u64,
  active: bool,
}

によって簡易的なユーザーアカウントのstructが定義できる.
structを実際に使うには,名前 { key: value, ...}とすれば,そのstructのinstanceを生成できる.必ずしもすべての値を指定する必要はない.

let user1 = User {
  email: String::from("hoge@fuga.com"),
  username: String::from("hogefuga"),
  active: true,
};

さらに,user1.emailのようにして,具体的な値を読み出せる.

Ownership of Struct Data

structが,他の変数が持っているheapへのreferenceを保持することは可能だが,それにはlifetime(chap.10)の導入が必要なので,この章ではstructの持つデータはreference以外とする.

An Example Program

四角形の面積を計算するプログラムを作る.

src/main.rs

struct Rectangle {
    length: u32,
    width: u32,
}

fn main() {
    let rect1 = Rectangle { length: 50, width: 30 };

    println!(
        "The area of the rectangle is {} square pixels.",
        area(&rect1)                                        // rect1をborrow
    );
}

fn area(rectangle: &Rectangle) -> u32 {                     // structのborrowも&名前
    rectangle.length * rectangle.width
}

shell

     Running `target/debug/rectangle`
The area of the rectangle is 1500 square pixels.

Adding Useful Functionality with Derived Traits

上のプログラムで,main内にprintln!("rect1 is {}", rect1);とすると,コンパイルエラーが生じる.これはprintln!({}, rect1)とすると,println!はrect1をDisplayという仕組みで表示しようとするが,structには,Displayによって何を表示するか明示されていないために起こる.ここで,println!("rect1 is {:?})とし,プログラム先頭に#[derive(Debug)]と書けば,rect1のすべてを表示してくれる.

shell

     Running `target/debug/rectangle`
rect1 is Rectangle { length: 50, width: 30 }

Method Syntax

Methodはfunctionに似た機能で,structに付属する.methodの最初の引数は必ずselfであり,selfとはそのmethodを呼び出したstructのinstanceのことである.

Defining Methods

structを定義したブロックと平行して,impl struct名{ fn …}としてmethodを定義できる.例えば

src/main.rs


#[derive(Debug)]
struct Rectangle {
  length: u32,
  width: u32,
}

impl Rectangle {
  fn area(&self) -> u32 {
    self.length * self.width
  }
}

fn main() { 
  let rect1 = Rectangle { length: 50, width: 30};
  println!{
    "The area of the rectangle is {} square pixels",
    rect1.area()
  };
}

shell

     Running `target/debug/rectangle`
The area of the rectangle is 1500 square pixels

このようにRectangleというstruct上のmethod, areaを定義できた.
methodによってinstanceのデータを変化させたいときは,methodの引数を&mut selfとする.ownershipを移してしまい時は単にselfとすればよい.

Methods with More Parameters

self以外に引数をもつmethodもfunctionの要領で定義できる.
他のRectangleを引数にとって,selfが他のRectangleを内側に含むことが出来るか否かを返す関数can_holdを定義する.

src/main.rs

fn main() {
  let rect1 = Rectangle {length: 50, width: 30};
  let rect2 = Rectangle {length: 40, width: 10};
  let rect3 = Rectangle {length: 45, width: 60};

  println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
  println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}

// struct Rectangle 略

impl Rectangle {
  fn area(&self) -> u32 {
    self.length * self.width
  }

  fn can_hold(&self, other: &Rectangle) -> bool {
    self.length > other.length && self.width > other.width
  }
}

shell

     Running `target/debug/rectangle`
Can rect1 hold rect2? true
Can rect1 hold rect3? false

Associated Functions

implにおいて,selfを引数に取らない関数も定義できる.こうして定義された関数をassociated functionという.String::fromはassociated functionの一つである.associated functionはよくconstructor, すなわちそのstructの新しいinstanceを生成するために実装される.たとえば

impl Rectangle {
  fn square(size: u32) -> Rectangle {
    Rectangle { length: size, width: size}
  }
}

は正方形を生成するassociated functionで,
lket sq = Rectangle::square(3)などとして呼ぶ.

0 件のコメント:

コメントを投稿