<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # Traits > [traits.md](https://github.com/rust-lang/rust/blob/master/src/doc/book/traits.md) commit 6ba952020fbc91bad64be1ea0650bfba52e6aab4 trait 是一個告訴 Rust 編譯器一個類型必須提供哪些功能語言特性。 你還記得`impl`關鍵字嗎,曾用[方法語法](#)調用方法的那個? ~~~ struct Circle { x: f64, y: f64, radius: f64, } impl Circle { fn area(&self) -> f64 { std::f64::consts::PI * (self.radius * self.radius) } } ~~~ trait 也很類似,除了我們用函數標記來定義一個 trait,然后為結構體實現 trait。例如,我們為`Circle`實現`HasArea` trait: ~~~ struct Circle { x: f64, y: f64, radius: f64, } trait HasArea { fn area(&self) -> f64; } impl HasArea for Circle { fn area(&self) -> f64 { std::f64::consts::PI * (self.radius * self.radius) } } ~~~ 如你所見,`trait`塊與`impl`看起來很像,不過我們沒有定義一個函數體,只是函數標記。當我們`impl`一個trait時,我們使用`impl Trait for Item`,而不是僅僅`impl Item`。 ### 泛型函數的 trait bound(Trait bounds on generic functions) trait 很有用是因為他們允許一個類型對它的行為提供特定的承諾。泛型函數可以顯式的限制(或者叫 [bound](#))它接受的類型。考慮這個函數,它并不能編譯: ~~~ fn print_area<T>(shape: T) { println!("This shape has an area of {}", shape.area()); } ~~~ Rust抱怨道: ~~~ error: no method named `area` found for type `T` in the current scope ~~~ 因為`T`可以是任何類型,我們不能確定它實現了`area`方法。不過我們可以在泛型`T`添加一個 trait bound,來確保它實現了對應方法: ~~~ # trait HasArea { # fn area(&self) -> f64; # } fn print_area<T: HasArea>(shape: T) { println!("This shape has an area of {}", shape.area()); } ~~~ `<T: HasArea>`語法是指`any type that implements the HasArea trait`(任何實現了`HasArea`trait的類型)。因為 trait 定義了函數類型標記,我們可以確定任何實現`HasArea`將會擁有一個`.area()`方法。 這是一個擴展的例子演示它如何工作: ~~~ trait HasArea { fn area(&self) -> f64; } struct Circle { x: f64, y: f64, radius: f64, } impl HasArea for Circle { fn area(&self) -> f64 { std::f64::consts::PI * (self.radius * self.radius) } } struct Square { x: f64, y: f64, side: f64, } impl HasArea for Square { fn area(&self) -> f64 { self.side * self.side } } fn print_area<T: HasArea>(shape: T) { println!("This shape has an area of {}", shape.area()); } fn main() { let c = Circle { x: 0.0f64, y: 0.0f64, radius: 1.0f64, }; let s = Square { x: 0.0f64, y: 0.0f64, side: 1.0f64, }; print_area(c); print_area(s); } ~~~ 這個程序會輸出: ~~~ This shape has an area of 3.141593 This shape has an area of 1 ~~~ 如你所見,`print_area`現在是泛型的了,并且確保我們傳遞了正確的類型。如果我們傳遞了錯誤的類型: ~~~ print_area(5); ~~~ 我們會得到一個編譯時錯誤: ~~~ error: the trait `HasArea` is not implemented for the type `_` [E0277] ~~~ ### 泛型結構體的 trait bound(Trait bounds on generic structs) 泛型結構體也從 trait bound 中獲益。所有你需要做的就是在你聲明類型參數時附加上 bound。這里有一個新類型`Rectangle<T>`和它的操作`is_square()`: ~~~ struct Rectangle<T> { x: T, y: T, width: T, height: T, } impl<T: PartialEq> Rectangle<T> { fn is_square(&self) -> bool { self.width == self.height } } fn main() { let mut r = Rectangle { x: 0, y: 0, width: 47, height: 47, }; assert!(r.is_square()); r.height = 42; assert!(!r.is_square()); } ~~~ `is_square()`需要檢查邊是相等的,所以邊必須是一個實現了[`core::cmp::PartialEq`](http://doc.rust-lang.org/cmp/trait.PartialEq.html) trait 的類型: ~~~ impl<T: PartialEq> Rectangle<T> { ... } ~~~ 現在,一個長方形可以用任何可以比較相等的類型定義了。 這里我們定義了一個新的接受任何精度數字的`Rectangle`結構體——講道理,很多類型——只要他們能夠比較大小。我們可以對`HasArea`結構體,`Square`和`Circle`做同樣的事嗎?可以,不過他們需要乘法,而要處理它我們需要了解[運算符 trait](#)更多。 ### 實現 trait 的規則(Rules for implementing traits) 目前為止,我們只在結構體上添加 trait 實現,不過你可以為任何類型實現一個 trait。所以從技術上講,你可以在`i32`上實現`HasArea`: ~~~ trait HasArea { fn area(&self) -> f64; } impl HasArea for i32 { fn area(&self) -> f64 { println!("this is silly"); *self as f64 } } 5.area(); ~~~ 在基本類型上實現方法被認為是不好的設計,即便這是可以的。 這看起來有點像狂野西部(Wild West),不過這還有兩個限制來避免情況失去控制。第一是如果 trait 并不定義在你的作用域,它并不能實現。這是個例子:為了進行文件I/O,標準庫提供了一個[`Write`](http://doc.rust-lang.org/nightly/std/io/trait.Write.html)trait來為`File`增加額外的功能。默認,`File`并不會有這個方法: ~~~ let mut f = std::fs::File::open("foo.txt").ok().expect("Couldn’t open foo.txt"); let buf = b"whatever"; // byte string literal. buf: &[u8; 8] let result = f.write(buf); # result.unwrap(); // ignore the error ~~~ 這里是錯誤: ~~~ error: type `std::fs::File` does not implement any method in scope named `write` let result = f.write(buf); ^~~~~~~~~~ ~~~ 我們需要先`use`這個`Write` trait: ~~~ use std::io::Write; let mut f = std::fs::File::open("foo.txt").expect("Couldn’t open foo.txt"); let buf = b"whatever"; let result = f.write(buf); # result.unwrap(); // ignore the error ~~~ 這樣就能無錯誤的編譯了。 這意味著即使有人做了像給`int`增加函數這樣的壞事,它也不會影響你,除非你`use`了那個trait。 這還有一個實現trait的限制。不管是trait還是你寫的`impl`都只能在你自己的包裝箱內生效。所以,我們可以為`i32`實現`HasArea`trait,因為`HasArea`在我們的包裝箱中。不過如果我們想為`i32`實現`Float`trait,它是由Rust提供的,則無法做到,因為這個trait和類型都不在我們的包裝箱中。 關于trait的最后一點:帶有trait限制的泛型函數是*單態*(*monomorphization*)(mono:單一,morph:形式)的,所以它是*靜態分發*(*statically dispatched*)的。這是什么意思?查看[trait對象](#)來了解更多細節。 ### 多 trait bound(Multiple trait bounds) 你已經見過你可以用一個trait限定一個泛型類型參數: ~~~ fn foo<T: Clone>(x: T) { x.clone(); } ~~~ 如果你需要多于1個限定,可以使用`+`: ~~~ use std::fmt::Debug; fn foo<T: Clone + Debug>(x: T) { x.clone(); println!("{:?}", x); } ~~~ `T`現在需要實現`Clone`和`Debug`。 ### where 從句(Where clause) 編寫只有少量泛型和trait的函數并不算太糟,不過當它們的數量增加,這個語法就看起來比較詭異了: ~~~ use std::fmt::Debug; fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) { x.clone(); y.clone(); println!("{:?}", y); } ~~~ 函數的名字在最左邊,而參數列表在最右邊。限制寫在中間。 Rust有一個解決方案,它叫“where 從句”: ~~~ use std::fmt::Debug; fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) { x.clone(); y.clone(); println!("{:?}", y); } fn bar<T, K>(x: T, y: K) where T: Clone, K: Clone + Debug { x.clone(); y.clone(); println!("{:?}", y); } fn main() { foo("Hello", "world"); bar("Hello", "world"); } ~~~ `foo()`使用我們剛才的語法,而`bar()`使用`where`從句。所有你所需要做的就是在定義參數時省略限制,然后在參數列表后加上一個`where`。對于很長的列表,你也可以加上空格: ~~~ use std::fmt::Debug; fn bar<T, K>(x: T, y: K) where T: Clone, K: Clone + Debug { x.clone(); y.clone(); println!("{:?}", y); } ~~~ 這種靈活性可以使復雜情況變得簡潔。 `where`也比基本語法更強大。例如: ~~~ trait ConvertTo<Output> { fn convert(&self) -> Output; } impl ConvertTo<i64> for i32 { fn convert(&self) -> i64 { *self as i64 } } // can be called with T == i32 fn normal<T: ConvertTo<i64>>(x: &T) -> i64 { x.convert() } // can be called with T == i64 fn inverse<T>() -> T // this is using ConvertTo as if it were "ConvertTo<i64>" where i32: ConvertTo<T> { 42.convert() } ~~~ 這突顯出了`where`從句的額外的功能:它允許限制的左側可以是任意類型(在這里是`i32`),而不僅僅是一個類型參數(比如`T`)。 ### 默認方法(Default methods) 關于trait還有最后一個我們需要講到的功能。它簡單到只需我們展示一個例子: ~~~ trait Foo { fn is_valid(&self) -> bool; fn is_invalid(&self) -> bool { !self.is_valid() } } ~~~ `Foo`trait的實現者需要實現`is_valid()`,不過并不需要實現`is_invalid()`。它會使用默認的行為。你也可以選擇覆蓋默認行為: ~~~ # trait Foo { # fn is_valid(&self) -> bool; # # fn is_invalid(&self) -> bool { !self.is_valid() } # } struct UseDefault; impl Foo for UseDefault { fn is_valid(&self) -> bool { println!("Called UseDefault.is_valid."); true } } struct OverrideDefault; impl Foo for OverrideDefault { fn is_valid(&self) -> bool { println!("Called OverrideDefault.is_valid."); true } fn is_invalid(&self) -> bool { println!("Called OverrideDefault.is_invalid!"); true // overrides the expected value of is_invalid() } } let default = UseDefault; assert!(!default.is_invalid()); // prints "Called UseDefault.is_valid." let over = OverrideDefault; assert!(over.is_invalid()); // prints "Called OverrideDefault.is_invalid!" ~~~ ### 繼承(Inheritance) 有時,實現一個trait要求實現另一個trait: ~~~ trait Foo { fn foo(&self); } trait FooBar : Foo { fn foobar(&self); } ~~~ `FooBar`的實現也必須實現`Foo`,像這樣: ~~~ # trait Foo { # fn foo(&self); # } # trait FooBar : Foo { # fn foobar(&self); # } struct Baz; impl Foo for Baz { fn foo(&self) { println!("foo"); } } impl FooBar for Baz { fn foobar(&self) { println!("foobar"); } } ~~~ 如果我們忘了實現`Foo`,Rust會告訴我們: ~~~ error: the trait `main::Foo` is not implemented for the type `main::Baz` [E0277] ~~~ ### Deriving 重復的實現像`Debug`和`Default`這樣的 trait 會變得很無趣。為此,Rust 提供了一個[屬性](#)來允許我們讓 Rust 為我們自動實現 trait: ~~~ #[derive(Debug)] struct Foo; fn main() { println!("{:?}", Foo); } ~~~ 然而,deriving 限制為一些特定的 trait: - [Clone](http://doc.rust-lang.org/core/clone/trait.Clone.html) - [Copy](http://doc.rust-lang.org/core/marker/trait.Copy.html) - [Debug](http://doc.rust-lang.org/core/fmt/trait.Debug.html) - [Default](http://doc.rust-lang.org/core/default/trait.Default.html) - [Eq](http://doc.rust-lang.org/core/cmp/trait.Eq.html) - [Hash](http://doc.rust-lang.org/core/hash/trait.Hash.html) - [Ord](http://doc.rust-lang.org/core/cmp/trait.Ord.html) - [PartialEq](http://doc.rust-lang.org/core/cmp/trait.PartialEq.html) - [PartialOrd](http://doc.rust-lang.org/core/cmp/trait.PartialOrd.html)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看