![](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 sliceはString
の一部への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 件のコメント:
コメントを投稿