Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#42] 5.20. Traits #16

Open
wants to merge 7 commits into
base: stable
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 67 additions & 105 deletions src/doc/trpl/traits.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
% Traits
# トレイト

Do you remember the `impl` keyword, used to call a function with [method
syntax][methodsyntax]?
あなたは[method syntax][methodsyntax]で関数を呼び出すために用いた`impl`キーワードを覚えていますか?

```rust
struct Circle {
Expand All @@ -17,10 +16,9 @@ impl Circle {
}
```

[methodsyntax]: method-syntax.html
[methodsyntax]: https://doc.rust-lang.org/stable/book/method-syntax.html

Traits are similar, except that we define a trait with just the method
signature, then implement the trait for that struct. Like this:
トレイトはそれに似ていますが、定義するときにはシグネチャだけを記述し、その後構造体毎に実装します。

```rust
struct Circle {
Expand All @@ -40,43 +38,35 @@ impl HasArea for Circle {
}
```

As you can see, the `trait` block looks very similar to the `impl` block,
but we don’t define a body, just a type signature. When we `impl` a trait,
we use `impl Trait for Item`, rather than just `impl Item`.
このように、`traits`の記述は`impl`とかなり似ているように見えますが、関数の実装は記述せず、シグネチャだけを定義します。トレイトを`実装`するときは、ただ`impl 実装の対象`とするのではなく、`impl トレイト for 実装の対象`とします。

We can use traits to constrain our generics. Consider this function, which
does not compile, and gives us a similar error:
ジェネリクスに制約を持たせるためにトレイトを使用できます。以下の関数について考えてみましょう。

```rust,ignore
fn print_area<T>(shape: T) {
println!("This shape has an area of {}", shape.area());
}
```

Rust complains:
これはコンパイルできませんが、このようなエラーが発生するはずです。

```text
error: type `T` does not implement any method in scope named `area`
```

Because `T` can be any type, we can’t be sure that it implements the `area`
method. But we can add a ‘trait constraint’ to our generic `T`, ensuring
that it does:
`T`はありとあらゆる型であるため、Rustは`.area()`メソッドが`T`について実装されているか確信を持てません。しかしジェネリック型`T`に対して'トレイトによる制約'を加えることで、`.area()`メソッドが実装されていることを保証できます。

```rust
# trait HasArea {
# fn area(&self) -> f64;
# }
trait HasArea {
fn area(&self) -> f64;
}
fn print_area<T: HasArea>(shape: T) {
println!("This shape has an area of {}", shape.area());
}
```

The syntax `<T: HasArea>` means `any type that implements the HasArea trait`.
Because traits define function type signatures, we can be sure that any type
which implements `HasArea` will have an `.area()` method.

Here’s an extended example of how this works:
`<T: HasArea>`構文は`HasAreaトレイトを実装するあらゆる型`を意味します。トレイトは関数のシグネチャを定義しているため、`HasArea`を実装するあらゆる型が`.area()`メソッドを持っていることを確認できます。
上記のコードを拡張した例がこちらです。

```rust
trait HasArea {
Expand Down Expand Up @@ -129,29 +119,26 @@ fn main() {
}
```

This program outputs:
このプログラムの出力は、

```text
This shape has an area of 3.141593
This shape has an area of 1
```

As you can see, `print_area` is now generic, but also ensures that we have
passed in the correct types. If we pass in an incorrect type:
上記の通り、`print_area`はジェネリックですが、正しい型が渡されることを保証します。もし不正な型が渡されるとすると、

```rust,ignore
print_area(5);
```

We get a compile-time error:
コンパイルエラーになります。

```text
error: failed to find an implementation of trait main::HasArea for int
```

So far, we’ve only added trait implementations to structs, but you can
implement a trait for any type. So technically, we _could_ implement `HasArea`
for `i32`:
これまでは構造体にのみトレイトの実装を追加していましたが、あなたはあらゆる型に対してトレイトを実装することができます。技術的には、`i32`のための`HasArea`を実装することも可能です。

```rust
trait HasArea {
Expand All @@ -169,25 +156,19 @@ impl HasArea for i32 {
5.area();
```

It is considered poor style to implement methods on such primitive types, even
though it is possible.
しかし例え可能であったとしても、プリミティブ型が持つメソッドを実装する方法としては望ましくないと考えられています。

This may seem like the Wild West, but there are two other restrictions around
implementing traits that prevent this from getting out of hand. The first is
that if the trait isn’t defined in your scope, it doesn’t apply. Here’s an
example: the standard library provides a [`Write`][write] trait which adds
extra functionality to `File`s, for doing file I/O. By default, a `File`
won’t have its methods:
ここまでくると何でもありな様に見えますが、手が負えなくなるのを防ぐためにトレイトの実装周りには2つの制限が設けられています。第1に、あなたのスコープ内で定義されていないトレイトは適用されません。例えば、標準ライブラリは`File`にI/O機能を追加するための[`write`][write]トレイトを提供します。デフォルトでは、`File`はそのメソッドを持っていません。

[write]: ../std/io/trait.Write.html
[write]: https://doc.rust-lang.org/std/io/trait.Write.html

```rust,ignore
let mut f = std::fs::File::open("foo.txt").ok().expect("Couldn’t open foo.txt");
let result = f.write("whatever".as_bytes());
# result.unwrap(); // ignore the error
result.unwrap(); // ignore the error
```

Here’s the error:
エラーは以下のとおりです。

```text
error: type `std::fs::File` does not implement any method in scope named `write`
Expand All @@ -196,44 +177,37 @@ let result = f.write(b"whatever");
^~~~~~~~~~~~~~~~~~
```

We need to `use` the `Write` trait first:
`Write`トレイトのために、初めに`use`が必要となります。

```rust,ignore
use std::io::Write;

let mut f = std::fs::File::open("foo.txt").ok().expect("Couldn’t open foo.txt");
let result = f.write("whatever".as_bytes());
# result.unwrap(); // ignore the error
result.unwrap(); // ignore the error
```

This will compile without error.
これはエラー無しでコンパイルされます。

This means that even if someone does something bad like add methods to `int`,
it won’t affect you, unless you `use` that trait.
これは、例え誰かが`int`へメソッドを追加するような望ましくない何かを行ったとしても、あなたがトレイトの`use`を行わない限り、影響はないことを意味します。

There’s one more restriction on implementing traits. Either the trait or the
type you’re writing the `impl` for must be defined by you. So, we could
implement the `HasArea` type for `i32`, because `HasArea` is in our code. But
if we tried to implement `Float`, a trait provided by Rust, for `i32`, we could
not, because neither the trait nor the type are in our code.
トレイトの実装についての制限はもう1つあります。トレイト、またはあなたが書いている`impl`の対象となる型は、あなた自身によって実装されなければなりません。`HasArea`は私たちが記述したコードであるため、`i32`型のための`HasArea`を実装することができます。しかし、`i32`のためにRustによって提供されている`ToString`トレイトを実装したいとしても、トレイトと型が共に私たちの記述したコードでないため、それはできません。

One last thing about traits: generic functions with a trait bound use
‘monomorphization’ (mono: one, morph: form), so they are statically dispatched.
What’s that mean? Check out the chapter on [trait objects][to] for more details.
最後に1つ。トレイトによって束縛されたジェネリック関数は`monomorphization`(mono:単一の、morph:形式)されるため、静的ディスパッチが行われます。一体どういう意味でしょうか?詳細については、[トレイトオブジェクト][to]の章をチェックしてください。

[to]: trait-objects.html
[to]: https://doc.rust-lang.org/stable/book/trait-objects.html

# Multiple trait bounds
# 複数のトレイト束縛

You’ve seen that you can bound a generic type parameter with a trait:
ここまで、トレイトによってジェネリックな型パラメータが束縛できることを見てきました。

```rust
fn foo<T: Clone>(x: T) {
x.clone();
}
```

If you need more than one bound, you can use `+`:
もし2つ以上の束縛が必要なのであれば、`+`を使うことができます。

```rust
use std::fmt::Debug;
Expand All @@ -244,15 +218,13 @@ fn foo<T: Clone + Debug>(x: T) {
}
```

`T` now needs to be both `Clone` as well as `Debug`.
`T`は今`Clone``Debug`、両方の実装が必要です。

# Where clause
# Where

Writing functions with only a few generic types and a small number of trait
bounds isn’t too bad, but as the number increases, the syntax gets increasingly
awkward:
ジェネリック型とトレイト束縛が少ないうちは良いのですが、数が増えるといよいよこの構文では不便になってきます。

```
``` rust
use std::fmt::Debug;

fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
Expand All @@ -262,12 +234,11 @@ fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
}
```

The name of the function is on the far left, and the parameter list is on the
far right. The bounds are getting in the way.
関数名は左端にあり、引数リストは右端にあります。トレイト束縛を記述する部分が邪魔になっているのです。

Rust has a solution, and it’s called a ‘`where` clause’:
そこでRustは'`where`節'と呼ばれる解決策を用意しています。

```
``` rust
use std::fmt::Debug;

fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
Expand All @@ -288,12 +259,9 @@ fn main() {
}
```

`foo()` uses the syntax we showed earlier, and `bar()` uses a `where` clause.
All you need to do is leave off the bounds when defining your type parameters,
and then add `where` after the parameter list. For longer lists, whitespace can
be added:
`foo()`は先程見せたままの構文で、`bar()`は`where`節を用いています。あなたは型パラメータの定義時ではなく、引数リストの後ろに`where`を追加することでトレイト束縛を記述できます。長いリストであれば、空白を加えることもできます。

```
``` rust
use std::fmt::Debug;

fn bar<T, K>(x: T, y: K)
Expand All @@ -306,11 +274,10 @@ fn bar<T, K>(x: T, y: K)
}
```

This flexibility can add clarity in complex situations.

`where` is also more powerful than the simpler syntax. For example:

```
この柔軟な構文により、複雑な状況であっても可読性を保つことができます。
また、`where`は基本的な構文よりも強力です。例えば、
(訳注: [std::convert::From](https://doc.rust-lang.org/std/convert/trait.From.html))
``` rust
trait ConvertTo<Output> {
fn convert(&self) -> Output;
}
Expand All @@ -319,27 +286,24 @@ impl ConvertTo<i64> for i32 {
fn convert(&self) -> i64 { *self as i64 }
}

// can be called with T == i32
// T == i32の時呼び出せる
fn normal<T: ConvertTo<i64>>(x: &T) -> i64 {
x.convert()
}

// can be called with T == i64
// T == i64のとき呼び出せる
fn inverse<T>() -> T
// this is using ConvertTo as if it were "ConvertFrom<i32>"
// これは"ConvertFrom<i32>"であるかのようにConvertToを用いている
where i32: ConvertTo<T> {
1i32.convert()
42.convert()
}
```

This shows off the additional feature of `where` clauses: they allow bounds
where the left-hand side is an arbitrary type (`i32` in this case), not just a
plain type parameter (like `T`).
ここでは`where`節の追加機能を披露しています。この節は型パラメータではない任意の型(`T`ではなく、今回は`i32`のような型)に対してトレイト束縛が可能です。

## Default methods
## デフォルトメソッド

There’s one last feature of traits we should cover: default methods. It’s
easiest just to show an example:
私たちがカバーしておくべきトレイトの最後の機能がデフォルトメソッドです。これの例を示すのは最も簡単です。

```rust
trait Foo {
Expand All @@ -349,15 +313,13 @@ trait Foo {
}
```

Implementors of the `Foo` trait need to implement `bar()`, but they don’t
need to implement `baz()`. They’ll get this default behavior. They can
override the default if they so choose:
`Foo`トレイトの実装者は`bar()`を実装する必要がありますが、`baz()`を実装する必要はありません。`baz()`にはデフォルトの動作が与えられているからです。実装者の選択次第ではこのデフォルトの動作をオーバーライドすることも可能です。

```rust
# trait Foo {
# fn bar(&self);
# fn baz(&self) { println!("We called baz."); }
# }
trait Foo {
fn bar(&self);
fn baz(&self) { println!("We called baz."); }
}
struct UseDefault;

impl Foo for UseDefault {
Expand All @@ -379,9 +341,9 @@ let over = OverrideDefault;
over.baz(); // prints "Override baz!"
```

# Inheritance
# 継承

Sometimes, implementing a trait requires implementing another trait:
時々、トレイトを実装するのに他のトレイトの実装が必要なこともあります。

```rust
trait Foo {
Expand All @@ -393,15 +355,15 @@ trait FooBar : Foo {
}
```

Implementors of `FooBar` must also implement `Foo`, like this:
`FooBar`の実装者は`Foo`もまた実装しなければなりません。以下のようになります。

```rust
# trait Foo {
# fn foo(&self);
# }
# trait FooBar : Foo {
# fn foobar(&self);
# }
trait Foo {
fn foo(&self);
}
trait FooBar : Foo {
fn foobar(&self);
}
struct Baz;

impl Foo for Baz {
Expand All @@ -413,7 +375,7 @@ impl FooBar for Baz {
}
```

If we forget to implement `Foo`, Rust will tell us:
もし`Foo`の実装を忘れると、Rustは以下のように知らせます。

```text
error: the trait `main::Foo` is not implemented for the type `main::Baz` [E0277]
Expand Down