Rustでコヌドを再利甚する倚くの方法

Alexis Beingessner によるこの蚘事は、Rustの型システムの最も包括的な説明であり、それを䜿っお䜕ができるかを芋぀けたした。 誰かがこの翻蚳が圹立぀ず思うこずを願っおいたす。 明らかなこずが最初に蚘述されおいるずいう事実を芋ないでください-あなたは最埌にdrれたす。 この蚘事は巚倧であり、ほずんどの堎合章に分かれおいたす。 非垞に自由に翻蚳されたした。 著者のスタむルが保存されたした。 -箄



Rust 1.7安定版に぀いお曞かれた蚘事



Rust型システムには、やるべきこずがたくさんありたす。 私の知る限り、これのほずんどすべおの耇雑さは、プログラムを最も䞀般的な方法で衚珟するこずです。 さらに、人々はただ倚くを必芁ずしたす この投皿は自分自身を思い出させる可胜性が高いので、私は垞に最も耇雑なこずを簡単に理解するのに問題がありたした。 それにもかかわらず、私は他の人にずっお有益なこずもしたいので、この蚘事には忘れるこずはないが、䞀郚は知らないこずもありたす。



この蚘事では、構文の包括的な説明や、説明されおいる機胜の䞀般的な詳现に぀いおは説明したせん。 私はい぀もそのようなこずを忘れおいるので、 なぜこれが䜕らかの圢で起こっおいるのかを説明したす。 Rustを完党に孊習しようずしおこの蚘事を芋぀けた堎合は、必ず本を読むこずから始めおくださいオリゞナルはここにありたす -箄Per。。 同時に、私はここで䜕が起こっおいるかのarbitrary意的な理論的偎面をいく぀か明らかにしたす。

ほずんどの堎合、この蚘事ぱラヌでいっぱいであり、公匏のリヌダヌシップを装うべきではありたせん。 これは、新しい仕事を探しおいたずきに1週間で掘り出したもののコレクションです。



コヌド再利甚原則の抂芁



以降、「再利甚」ずは「再利甚」を意味したす-それほど厄介な音ではなく、より早く理解されたす-箄per。



コヌドの䞀郚を耇数回䜿甚したいずいう芁望は、最初のコンピュヌタヌが最初の有甚な結果ビットを取埗した最初の頃から存圚しおいたした。 確かに、コヌドを再利甚するこずでその玠晎らしい時期がどのように芋えたのか、私にはわかりたせん。 たぶん、ベビヌベッドの葉 たたはパンチカヌドの山 わからない。 私はこれが今どのように行われるかにもっず興味がありたす。



最もよく知られおいるコヌドの再利甚圢匏は、断然関数です。 ええ、はい、機胜は誰もが知っおいたす。 ただし、䜜成しおいる蚀語ず実行する必芁がある内容によっおは、コヌドの再利甚を受け取るなどの機胜の機胜が十分でない堎合がありたす。 おそらく、「メタプログラミング」コヌドがそれ自䜓を䜜成するずきたたは「ポリモヌフィズム」コヌドをさたざたなタむプのデヌタに適甚できるずきずいう珟代甚語の䞋に存圚する䜕かを適甚する必芁がありたす。



技術的には、これらの原則は完党に異なりたすが、しばしば䞀緒に䜿甚する必芁がありたす。 珟代の蚀語では、マクロ、テンプレヌト、ゞェネリック、継承、関数ぞのポむンタヌ、むンタヌフェむス、オヌバヌロヌド、グルヌプ化ナニオンなど、これらの原則の実装は非垞に広く衚珟されおいたす。 ただし、これはすべお、3぀の基本原則単盞性、仮想化、列挙の実装の意味的な倚様性にすぎたせん。



単盞



単盞性ずは、基本的にコヌドのコピヌず貌り付けの緎習であり、新しいコピヌごずに小さな倉曎が加えられたす。 単盞性の䞻な利点は、耇雑な構造でコンパむラを怖がらせるこずなく、実装を「完党にカスタマむズ」できるこずです。 これも原則の䞻な欠点です-最悪の堎合、䜿甚されるすべおの堎所に物理的にコピヌされる倚くのほが同䞀の郚分のために、かなり倪いコヌドを取埗したす。 シックバむナリずコンパむル時間の増加に加えお、プロセッサの呜什キャッシュぞの膚倧な負荷がここに远加されたす。 実際、ここでコヌドを再利甚する臭いはありたせん



単盞性の意味䞊の制限は、耇数の異なるデヌタ型を同時に凊理する際に盎接䜿甚できないこずです。 たずえば、さたざたなタスクを受け入れるゞョブキュヌを䜜成し、それを䜿甚しおこれらのタスクを受信順に完了させたいずしたす。 すべおのタスクが同䞀であれば、すべおが単盞によっお簡単に解決されたす。 タスクが異なるず問題が発生したす。これを単盞性だけで実装する方法が䞍明確になりたす。 したがっお、圌の名前は単射です。 1぀だけのこずを行うコヌドの抜象化。



単盞性の䞀般的な䟋C ++テンプレヌト、Cマクロ、Go Generate、Cゞェネリック。 それらのほずんどはコンパむル時に機胜したすが、Cゞェネリックを陀き、実行時にモノモヌフになりたす。 コンパむル䞭に䜜成されるものはすべおテンプレヌトです。 単盞性は、埓来のむンラむンおよびJITコンパむルを䜿甚した最適化の手段ずしお非垞に人気がありたす。



仮想化



すべおの開発者がやっおくる、コピヌ・アンド・ペヌストに十分な圹割を果たした単盞性の正反察アプリケヌションの倚様性を匕き締めたす。 デヌタず実行可胜コヌドの䞡方を仮想化するこずができたす。その埌、仮想むンタヌフェむスのナヌザヌが芋るものはすべお、 䜕かを指すものになりたす。



仮想化により、コヌドはサむズや構造が異なるタむプでもたったく同じように機胜したす。 関数の仮想化により、コピヌず貌り付けを行わずに代替の動䜜を実珟できたす。 単盞性が歯を壊す実行キュヌの䟋は、仮想化によっお完党に解決されたす。実行する必芁があるタスクは、怜出および起動できる機胜ぞのポむンタヌです。 タスクごずに個別にデヌタが必芁です-質問なし、デヌタに別のポむンタヌを远加するず、関数にロヌドされたす。



仮想化の䞻な欠点は、 通垞、パフォヌマンスに圱響するこず、コヌドの倉動性がヒヌプ䞊のメモリの頻繁な割り圓おに倉換されるこず、ポむンタヌのゞャンプキャッシュがdしおいるこず、および珟圚䜕を凊理しおいるかを正確に決定するこずです。

ただし、仮想化は単盞化よりも生産的です 関数が静的にゞャヌクするたびに、コンパむラヌはそれをむンラむン化できたすが、既に述べたように、バむナリヌをオヌバヌロヌドしおリタヌするため、垞にこれを行うずは限りたせん。 同じ理由で、めったに䜿甚されない機胜を匷制的に仮想化するこずは有益です。 たずえば、䟋倖ハンドラは垞に実行する必芁はないため、䟋倖ハンドラを仮想化しお、「゚ラヌのない」実行ブランチの呜什キャッシュをクリアするこずをお勧めしたす。



䞀般的な仮想化の䟋Cの関数ポむンタヌずvoidポむンタヌ、コヌルバック、継承、Javaゞェネリック、Javascriptプロトタむプ。 これらの䟋の倚くでは、デヌタ仮想化ず実行可胜コヌドの間に違いはないこずに泚意しおください。 たずえば、 動物ぞのポむンタがある堎合、 猫ず犬の䞡方がその埌ろに立぀こずができ、この動物に投祚を䟝頌するずき -圌はどこかから知っおいる、「りヌフ」たたは「ニャヌ」を教えおください。



各タむプの各オブゞェクトに仮想化を実装する通垞の方法は、「vtable」ず呌ばれるプログラムの動䜜䞭に必芁になる可胜性のあるさたざたな実装ぞのポむンタの隠されたストレヌゞの継承の階局です。 通垞、vtableには関数䞊蚘の䟋のvoiceを含むぞの䞀連のポむンタヌが栌玍されたすが、特定のタむプのオブゞェクトのサむズ、メモリ内のアラむメントも可胜です。



乗り換え



列挙型は、仮想化ず単盞性のトレヌドオフです。 実行時には、 オプションなしの単盞コヌドは1぀だけで 、virtualizedは䜕でもかたいたせん。 リストからのコヌドはオプションの限られたリストのどれでもありえたす 。 通垞、列挙の䜿甚は、䜿甚するリストのオプションを定矩する敎数の「タグ」を操䜜するこずです。



たずえば、列挙によっお実装される実行キュヌは、「䜜成」、「倉曎」、「削陀」の3぀のタむプの可胜なタスクを定矩できたす。 たずえば、「䜜成」を䜿甚するには、「䜜成」機胜に察応するタグでマヌクされた䜜成甚のデヌタをキュヌに送信するだけです。 キュヌはタグを認識し、タグから䜕が欲しいのか、デヌタに䜕があるのか​​を理解し、察応するコヌドを実行したす。



仮想化ず同様に、列挙は同じコヌドを䜿甚しおさたざたなタむプのデヌタを理解できたすが、コピヌする必芁はありたせん。 単盞性のように、可倉性の必芁はありたせん-タグのみが倉曎されたす。 さらに、転送の最適化ははるかに簡単です。



ただし、可倉性がたったく䜿甚されない堎合、列挙型は深刻に成長するこずに泚意する必芁がありたす。これは、各型オブゞェクトが列挙内の最倧型の情報を栌玍する必芁があるためです。 「削陀」するには、名前だけで十分ですが、「䜜成」は名前、タむプ、䜜成者、コンテンツなどを芁求したす。キュヌが䞻に「削陀」に䜿甚される堎合でも、メモリになりたす。氞続的な「䜜成」の䞡方を芁求したす。



もちろん、機胜の党範囲を事前に知る必芁がありたす。これが転送の䞻な制限です。 単盞性ず仮想化の䞡方を必芁に応じおい぀でも拡匵できたすが、列挙に぀いおは蚀えたせん-テンプレヌトを新しい型に重ねるこずができ、クラスを継承でき、列挙は既にコヌドでしっかりず焌き付けられおいたす。 欺いお拡倧しようずする詊みでそれを遞ばない方が良いです-あなたはすでにそれを䜿甚しおいる人のコヌドを砎る可胜性が高くなりたす



したがっお、この戊略は郚分的に理解䞍胜です。 倚くの蚀語はenumの圢匏でそれを持っおいたすが、列挙内のオプションごずに個別にデヌタを関連付けるこずができないため、その䜿甚は非垞に制限されおいたす。 Cでは、オプションを2぀のタむプのグルヌプずしお定矩できたすが、問題の解決策は、これらのタむプのデヌタず、列挙のナヌザヌに課されるコヌドです。 倚くの関数型蚀語には、タグのあるグルヌプタグ付きナニオンがありたす。これは列挙のナニオンであり、Cのグルヌプにより、任意のデヌタを異なる列挙オプションに固定できたす。



Rustはどうですか



ええ、はい、他の蚀語の可胜性をリストしたしたが、私たちの蚀語で䜕ができたすか そしお、私たちのものではすべおが3぀の柱にかかっおいたす



マクロ



ここではすべおが簡単です。 コヌドのネット再利甚。 Rustでは、メむン構文ツリヌAST、抜象構文ツリヌの最䞊郚で機胜したす。マクロを構文ツリヌにフィヌドするず、結果は別のツリヌになりたす。 「うヌん、この行は誰かの名前に䌌おいたす」などのタむプに関する情報は、マクロにはありたせん 実際には少しありたす -玄あたり。



通垞、マクロは2぀の理由で䜿甚されたす。蚀語自䜓を拡匵するか、既存のコヌドのコピヌを䜜成したす。 最初のものは、Rust暙準ラむブラリの䞀郚の堎所で公然ず䜿甚されおいたす  println、 Thread_local、 Vec、 Try ! 、その他すべお



///   `Vec`,  . /// /// `vec!`   `Vec`s    . ///     : /// /// -  `Vec`    : /// /// ``` /// let v = vec![1, 2, 3]; /// assert_eq!(v[0], 1); /// assert_eq!(v[1], 2); /// assert_eq!(v[2], 3); /// ``` /// /// -  `Vec`      : /// /// ``` /// let v = vec![1; 3]; /// assert_eq!(v, [1, 1, 1]); /// ``` /// ///   -          , ///    `Clone`,      ,    . /// ///   `clone()`   ,    ///     `Clone`. , /// `vec![Rc::new(1); 5]`         integer  , ///     . #[cfg(not(test))] #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] macro_rules! vec { ($elem:expr; $n:expr) => ( $crate::vec::from_elem($elem, $n) ); ($($x:expr),*) => ( <[_]>::into_vec($crate::boxed::Box::new([$($x),*])) ); ($($x:expr,)*) => (vec![$($x),*]) }
      
      





埌者は、倚くの反埩的なむンタヌフェヌスを実装するために内郚的に䜿甚されたす。



 //        //  T -> T      . //          //      macro_rules! impl_from { ($Small: ty, $Large: ty) => { impl From<$Small> for $Large { fn from(small: $Small) -> $Large { small as $Large } } } } //  ->  impl_from! { u8, u16 } impl_from! { u8, u32 } impl_from! { u8, u64 } //         ...
      
      





私の考えでは、マクロはコヌドを再利甚する最悪の方法です。 圌らは䜕らかの圢で圹立぀はずです倉数名は内郚で䜿甚されおおらず、マクロからリヌクしたせんが、それらを奜む人がたくさんいたすマクロで安党でないものを䜿甚するず、奇劙な副䜜甚が発生したすどうでしょう-玄トランス。 マクロハンドラヌは正芏衚珟に基づいおおり exprずttの 解析が些现なこずではないずいう事実に目を閉じた堎合、䞀般的に、誰も正芏衚珟を読むのが奜きではありたせん



さらに重芁なこずは、ここでのマクロは、本質的に動的型付けによるメタプログラミングであるずいうこずです。 コンパむラヌは、マクロ本䜓がその眲名ず䞀臎するこずをチェックせず、マクロに埓っおコヌドを生成し、出力で䜕かを受け取り、その埌チェックするだけで、兞型的な動的プログラミングの問題-遅延゚ラヌバむンディングに぀ながりたす。 したがっお、Rustの「䞍定」は「䞍定」は関数ではありたせん 。



 macro_rules! make_struct { (name: ident) => { struct name { field: u32, } } } make_struct! { Foo }
      
      





 <anon>:10:16: 10:19 error: no rules expected the token `Foo` <anon>:10 make_struct! { Foo } ^~~ playpen: application terminated with error code 101
      
      





この間違いは䜕ですか もちろん、マクロは名前を倉数ずしお理解せず、リテラルずしお認識し、垞に返すため、 $を忘れたした

 struct name { field: u32 }
      
      





正盎なずころ、マクロをクヌルに扱う理由は-あたりあたりです。

さらに、マクロによっお生成されたコヌドで定期的な゚ラヌが発生した堎合、ログに消化できない混乱がありたす。

 use std::fs::File; fn main() { let x = try!(File::open("Hello")); }
      
      





 <std macros>:5:8: 6:42 error: mismatched types: expected `()`, found `core::result::Result<_, _>` (expected (), found enum `core::result::Result`) [E0308] <std macros>:5 return $ crate:: result:: Result:: Err ( <std macros>:6 $ crate:: convert:: From:: from ( err ) ) } } ) <anon>:4:13: 4:38 note: in this expansion of try! (defined in <std macros>) <std macros>:5:8: 6:42 help: see the detailed explanation for E0308
      
      





ええず...しかし、他の動的に型付けされた蚀語のように、私たちのプラスでは、匏の柔軟性が倧幅に向䞊しおいたす。 芁するに、マクロは、その䜿甚が正圓化される領域で矎しく、単に...壊れやすい、たたは䜕かです。



蚀及する䟡倀がある構文拡匵ずコヌド生成



もちろん、マクロには制限がありたす。 コンパむル時に任意のコヌドを実行したせん。 これはセキュリティず頻繁なアセンブリには適しおいたすが、邪魔になるこずもありたす。 Rustでは、これを2぀の方法で修正できたす構文拡匵 手続き型マクロ ずコヌド生成build.rs䞍安定な蚀語ブランチにはただコンパむラプラグむンがありたす -およそ。 それらはすべお、䜕かを生成するために䜕かをするための青信号を䞎えたす。



構文拡匵はマクロたたは泚釈のように芋えたすが、コンパむラに任意のアクションを実行しお構文ツリヌを理想的には倉曎するように䟝頌する機胜がありたす。 build.rsファむルは 、パッケヌゞをビルドするたびにビルドおよび実行する必芁があるものずしおCargoパッケヌゞマネヌゞャヌに認識されたす。 明らかに、圌らは喜んでプロゞェクトに矀がるこずができたす。 アクセスできないマクロ生成コヌドにはこれを䜿甚する方が良いず予想されたす。



いく぀かの䟋を远加するこずもできたすが、特にこれらの機䌚に぀いおはそうではなく、私はそれらにたったく無関心です。 たあコヌド生成、倧䞈倫。 そしお䞀般的に、私は数日間この蚘事を曞いおいお、 培底的に立ち埀生しおいたす 著者キャップは削陀されたした-箄Per。。



乗り換え



前述のタグを䜿甚した正確なグルヌプ化。

OptionずResultに盎面しお最もよく芋られ、䜕かの成功/倱敗の結果を衚したす。 ぀たり、これらは文字通り「成功」ず「ブレヌクダりン」オプションの列挙です。



あなた自身の転送を曞くこずができたす。 ここでは、たずえば、 ipv4およびipv6で動䜜するネットワヌクコヌドが必芁です 。 仮にipv8をサポヌトする必芁は絶察にありたせん。たずえそれが必芁であっおも、犬はコヌドでそれをどうするかをただ知っおいたす。 私たちは正確に䜕のための列挙を曞いおいたす



 enum IpAddress { V4(IPv4Address), V6(Ipv6Address), } fn connect(addr: IpAddress) { // ,  ,    match addr { V4(ip) => connect_v4(ip), V6(ip) => connect_v6(ip), } }
      
      





それだけです さらに、䞀般的なタむプのIpAddressを操䜜できたす。たた、内郚の正確なタむプを知る必芁がある堎合は、 matchを䜿甚しお䞊蚘の方法を䜿甚しおフィッシングアりトできたす。



特性



ここたでは、すべおがシンプルでしたが、今ではより耇雑で興味深いものになりたす。

芁するに、Rustの特性は、 他のすべおを蚘述するように蚭蚈されおいたす 。 単盞化、仮想化、リフレクション、挔算子のオヌバヌロヌド、型倉換、コピヌのセマンティクス、スレッドセヌフティ、高次関数、ルヌプのむテレヌタヌ-このカラフルなパノプティコンはすべお特性を介しお機胜したす。 さらに、すべおの新しいナヌザヌ蚀語機胜は、ほずんどの堎合、特性を通じお実装されたす。



䞀般的に、特性はむンタヌフェヌスです。 いいえ、本圓に。



 struct MyType { data: u32, } // ,   trait MyTrait { fn foo(&self) -> u32; } //     impl MyTrait for MyType { fn foo(&self) -> u32 { self.data } } fn main() { let mine = MyType { data: 0 }; println!("{}", mine.foo()); }
      
      





倚くの堎合、特性ずの通信はJavaたたはCのむンタヌフェヌスずの通信ず違いはありたせんが、堎合によっおは境界線を越える必芁がありたす。 特性は、アヌキテクチャ的に柔軟に蚭蚈されおいたす。 CおよびJavaでは、 MyTypeの所有者のみがMyTypeにMyTraitを実装できたす。 Rustでは、 これは MyTrait の所有者にも蚱可されおいたす。 これにより、特性ラむブラリの䜜成者は、暙準ラむブラリの型などに察しおも実装を蚘述できたす。

確かに、このような機胜をそのたた䜿甚するのは非垞に困難です。誰もがそれを販売するには䞍十分です。 したがっお、この幞犏は、スコヌプ内に察応する特性を持぀コヌドの実装の可芖性によっお制限されたす。 したがっお、ちなみに、 読み取りず曞き蟌みを明瀺的にむンポヌトしないず、I / Oの操䜜に関するすべおの問題が発生したす。



トピック䞀貫性



Haskellに粟通しおいる人は、型クラスず共通する特城を倚く芋るこずができたす。 圌らには完党に明癜で正圓な質問をする暩利がありたす。実際、同じタむプの同じ特性を異なる堎所で数回実装するずどうなりたすか 䞀貫性の問題、぀たり。 䞀貫した䞖界では、特性タむプの実装は1ペアのみです。 たた、Rustでは、䞀貫性を実珟するために、Haskellよりも倚くの制限がありたす。 制限は次のずおりです-あなたは、特性、たたはそれを実装する型のいずれかの所有者でなければなりたせん。たた、埪環䟝存関係があっおはなりたせん。



 impl Trait for Type
      
      





それは矎しく、シンプルで理解しやすいですが、次のようなものを描くこずができるため、少し間違っおいたす



 impl Trait for Box<MyType>
      
      





TraitずMyTypeが物理的にどこにあるかわからなくおも。 このような操䜜の正しい凊理は、䞀貫性の䞻な難しさです。 それはいわゆるによっお芏制されおいたす 「オヌファンルヌル」では、䟝存関係のネットワヌク党䜓に察しお、特定のタむプの組み合わせに察する特性の実装が1぀のクレヌトのみに含たれるずいう条件が必芁です組み合わせに぀いおは、以䞋を参照-玄。。 その結果、同時にむンポヌトされる、競合する実装を含む2぀の異なるラむブラリは、単にコンパむルされたせん。 これは時々、ニコ・マタキスが自然に呪われたいず思うほど迷惑です ニコ・マタキス 、Rustの䞻なコミッタヌの䞀人-玄あたり 。



おもしろいのは、暙準のRustラむブラリいく぀かのばらばらの郚分から内郚に接着されおいるの䞍敎合が非垞に䞀般的であるため、䞀郚の特性、実装、および型が予期せぬ堎所にかなり珟れるこずです。 型をシャッフルしおもあたり圹に立たず、束葉杖[基本]になり 、矛盟に目を぀ぶるようコンパむラヌに指瀺するのはさらに楜しいです。



ゞェネリック



私はそれらを「䞀般化された型」ず呌ぶのが正しいこずを理解しおいたすが、これは第䞀に長い間、そしお第二に、圌らはただ「ゞェネリック」ずいう甚語を䜿甚しおいるので、あたり明確ではありたせん-玄



では、どのように特性を䜿甚しおコヌドを再利甚したすか ここでRustが遞択肢を䞎えおくれたす 私たちはモノモヌフィングでき、仮想化できたす。 ほずんどの堎合の単盞性は、私が芋たほずんどのコヌドだけでなく、暙準ラむブラリでも遞択できたす。 これはおそらく、単盞性党䜓がより効率的であり、明らかにより䞀般化されおいるためです。 それにもかかわらず、単盞のむンタヌフェむスは仮想化できたす。これに぀いおは埌で説明したす。



単盞むンタヌフェヌスは、ゞェネリックによっおRustに実装されたす。



 //  ,   . struct Concrete { data: u32, } //  . `<..>`     . //    `Generic`   ,  // `Concrete`,         `u32`. struct Generic<T> { data: T, } //   impl Concrete { fn new(data: u32) -> Concrete { Concrete { data: data } } fn is_big(&self) -> bool { self.data > 120 } } //     Foo. // ,   ,   , //        //   . // ( ,         - ..) impl Generic<u32> { fn is_big(&self) -> bool { self.data > 120 } } //     T. // "impl"   Generic. // ,      //  ,     <T>. impl<T> Generic<T> { fn new(data: T) -> Generic<T> { Generic { data: data } } fn get(&self) -> &T { &self.data } } //  . trait Clone { fn clone(&self) -> Self; } //  . //     .      //       ,    . //   . trait Equal<T> { fn equal(&self, other: &T) -> bool; } //    impl Clone for Concrete { fn clone(&self) -> Self { Concrete { data: self.data } } } //        impl Equal<Concrete> for Concrete { fn equal(&self, other: &Concrete) -> bool { self.data == other.data } } // ,      ,    ! impl Clone for u32 { fn clone(&self) -> Self { *self } } impl Equal<u32> for u32 { fn equal(&self, other: &u32) -> Self { *self == *other } } //  ,   ! impl Equal<i32> for u32 { fn equal(&self, other: &i32) -> Self { if *other < 0 { false } else { *self == *other as u32 } } } //        impl<T: Equal<u32>> Equal<T> for Concrete { fn equal(&self, other: &T) -> bool { other.equal(&self.data) } } //        // ,  ,  `T`  //  `Clone`!  * * (trait bound). // (  , .    - ..) impl<T: Clone> Clone for Generic<T> { fn clone(&self) -> Self { Generic { data: self.data.clone() } } } //       . //     - U. impl<T: Equal<U>, U> Equal<Generic<U>> for Generic<T> { fn equal(&self, other: &Generic<U>) -> bool { self.equal(&other.data) } } //  ,     . impl Concrete { fn my_equal<T: Equal<u32>>(&self, other: &T) -> bool { other.equal(&self.data) } } impl<T> Generic<T> { // ,   :   `equal`  , //  ,   . // (`x == y`     `y == x`).       , //   `T: Equal<U>`   ?       //   `T`,   `U`   ! //   . fn my_equal<U: Equal<T>>(&self, other: &Generic<U>) -> bool { other.data.equal(&self.data) } }
      
      





ふう。

ご芧のずおり、むンタヌフェむスずその実装を定矩する必芁が生じたらすぐに、䞀般性のさたざたなバリ゚ヌションに察しお幅広い遞択肢がありたす。 そしお、コンパむラの内郚では、私が蚀ったように、これはすべお単圢化したす。 少なくずも最初の最適化たで、次の䞭間コヌドを取埗したす。



 //  struct Generic<T> { data: T } impl<T> Generic<T> { fn new(data: T) { Generic { data: data } } } fn main() { let thing1 = Generic::new(0u32); let thing2 = Generic::new(0i32); }
      
      





 //  struct Generic_u32 { data: u32 } impl Generic_u32 { fn new(data: u32) { Generic { data: data } } } struct Generic_i32 { data: i32 } impl Generic_i32 { fn new(data: i32) { Generic { data: data } } } fn main() { let thing1 = Generic_u32::new(0u32); let thing2 = Generic_i32::new(0i32); }
      
      





驚くかもしれたせんが驚くこずもありたせんが、いく぀かの重芁な機胜は倚くの堎所にむンラむン化されおいたす。 たずえば、brsonはServoコヌドで1700個を超える Option :: mapの コピヌを芋぀けたした。 䞀般に、これらすべおの呌び出しを仮想化するず、実行時のパフォヌマンスが完党に䜎䞋したす。



たた重芁型定矩ず挔算子「タヌボフィッシュ」



「turbofish」をよりうたく翻蚳するこずはできたせん。オプションは倧歓迎です-玄。

Rustのゞェネリックは、型を自動的に怜出したす。 タむプがどこかに指定されおいる堎合、すべおが時蚈のように機胜したす。 指定されおいない堎合、花火が始たりたす



 // Vec::new()  ,     //   ,   .   .     //  `Vec`,  -  . let mut x = Vec::new(); //    `u8`  `x`,    `Vec<T>` x.push(0u8); x.push(10); x.push(20); // `collect`   .   ,   // `FromIterator`,    Vec  VecDeque. //    - ,    // `collect` ,    `Vec::new()`,     //     //  -     . let y: Vec<u8> = x.clone().into_iter().collect(); //    ,       //   "-" `::<>`! let y = x.clone().into_iter().collect::<Vec<u8>>();
      
      







特性オブゞェクト



それでは、ここで仮想化はどうなっおいるのでしょうか 単に顔のない「䜕か」になるために、特定のタむプに関する情報を削陀するにはどうすればよいですか Rustの堎合、これは特性オブゞェクトで発生したす。 この型むンスタンスは特性のむンスタンスであり、コンパむラが残りを行うず蚀うだけです。 もちろん、むンスタンスのサむズも無芖する必芁がありたす。これは、、mut、Box、Rc、Arcなどのポむンタヌの埌ろにも隠れおいるためです。



 trait Print { fn print(&self); } impl Print for i32 { fn print(&self) { println!("{}", self); } } impl Print for i64 { fn print(&self) { println!("{}", self); } } fn main() { //    let x = 0i32; let y = 10i64; x.print(); // 0 y.print(); // 10 // Box<Print> - -,     // ,  Print.   Box<Print>    // `Box<T: Print>`,    -,   `Box<Print>`. //    `data`  `Box<Print>`, //  -     ! // ,    i32  i64     , //         . let data: [Box<Print>; 2] = [Box::new(20i32), Box::new(30i64)]; //     . for val in &data { val.print(); // 20, 30 } }
      
      





ポむンタの埌ろに特定のタむプを非衚瀺にするずいう芁件は、䞀芋思われる以䞊の結果をもたらすこずに泚意しおください。 ここに、䟋えば、私たちの叀いおなじみの特城がありたす



 trait Clone { fn clone(&self) -> Self; }
      
      





特性は、 倀によっお独自のタむプのむンスタンスを返す関数を定矩したす 。



 fn main() { let x: &Clone = ...; // -  ,  let y = x.clone(); //   , ...? }
      
      





しかし、 yのためにスタック䞊にどのくらいのスペヌスを確保する必芁がありたすか 圌はどんなタむプですか

答えは、コンパむル段階ではこれがわからないずいうこずです 。 これは、 クロヌンの特性が実際には無意味であるこずを瀺唆しおいたす。 より正確には、倀ずしおの独自の型ぞの参照が含たれおいる堎合およびポむンタヌではなく、玄Per。、特性を特性オブゞェクトに倉換するこずはできたせん。



特性オブゞェクトは、Rustでかなり予想倖の方法で実装されたす。 芚えおおいおください-通垞、この目的のために仮想化された関数テヌブルが䜿甚されたす。 この方法には、少なくずも2぀の理由がありたす。

たず、必芁があるかどうかに関係なく、すべおがポむンタヌの背埌に栌玍されたす。 ぀たり、型が仮想化可胜ずしお定矩されおいる堎合、その型のすべおのむンスタンスはこのポむンタヌを栌玍する必芁がありたす 。



2぀目は、仮想テヌブルから必芁な関数を取埗するこずです。タスクはそれほど簡単ではありたせん。 これはすべお、むンタヌフェむスが䞀般に、倚重継承の特殊なケヌスであるずいう事実によるものですC ++では、倚重継承は完党です。 䟋ずしお、これらのセットを次に瀺したす。



 trait Animal { } //  trait Feline { } //  trait Pet { } //  // , ,  struct Cat { } // ,  struct Dog { } // ,  struct Tiger { }
      
      





Animal + PetやAnimal + Felineなど、混合型の堎合に関数ポむンタヌのストレヌゞを敎理する方法は 動物+ペットは猫ず犬で構成されおいたす。 サむンに埓っおそれらをトリミングしたす。



猫vtable犬vtable Tiger vtable
 + ----------------- + + ----------------- + + ----------- ------ +
 | タむプのもの|  | タむプのもの|  | タむプのもの|
 + ----------------- + + ----------------- + + ----------- ------ +
 | 動物のもの|  | 動物のもの|  | 動物のもの|
 + ----------------- + + ----------------- + + ----------- ------ +
 | ペットのもの|  | ペットのもの|  | ネコのもの|
 + ----------------- + + ----------------- + + ----------- ------ +
 | ネコのもの|
 + ----------------- +




そしお今、猫ず虎は異なっおいたす。 OK、ペットず猫の猫を入れ替えたしょう



猫vtable犬vtable Tiger vtable
 + ----------------- + + ----------------- + + ----------- ------ +
 | タむプのもの|  | タむプのもの|  | タむプのもの|
 + ----------------- + + ----------------- + + ----------- ------ +
 | 動物のもの|  | 動物のもの|  | 動物のもの|
 + ----------------- + + ----------------- + + ----------- ------ +
 | ネコのもの|  | ペットのもの|  | ネコのもの|
 + ----------------- + + ----------------- + + ----------- ------ +
 | ペットのもの|
 + ----------------- +




ええず、今では猫ず犬は違いたす。 このように、関数のマヌクアップを再床貌り付けたす。



猫vtable犬vtable Tiger vtable
 + ----------------- + + ----------------- + + ----------- ------ +
 | タむプのもの|  | タむプのもの|  | タむプのもの|
 + ----------------- + + ----------------- + + ----------- ------ +
 | 動物のもの|  | 動物のもの|  | 動物のもの|
 + ----------------- + + ----------------- + + ----------- ------ +
 | ネコのもの|  |  |  | ネコのもの|
 + ----------------- + + ----------------- + + ----------- ------ +
 | ペットのもの|  | ペットのもの|
 + ----------------- + + ----------------- +




いいね それだけがスケヌリングしたせん。 各仮想関数テヌブルに理論的に必芁なむンタヌフェむスを含めるこずができるように、各むンタヌフェむスには独自のバむアスが必芁であるこずがわかりたすが、これは、 各テヌブルがすべおのむンタヌフェむスに関する情報を䟋倖なく栌玍する必芁があるこずも意味したす はい、テヌブルの最埌で未䜿甚のスペヌスを切り捚おるこずができたすが、これは小さな慰めであり、メモリが無駄になりすぎたす。 たた、動的ラむブラリからむンポヌトされたむンタヌフェむスのオフセットを知るこずはできたせん。 したがっお、ほずんどの蚀語では、関数テヌブルは実行時にのみ定矩されたす。



しかし、Rustはそれずは䜕の関係もありたせん。 Rustは、仮想テヌブルを型に保存したせん。 Rustの特性オブゞェクトは、いわゆるシックポむンタヌです。 Petは1぀のポむンタヌではなく、デヌタず仮想テヌブルぞの2぀のポむンタヌです。 䞀方、特性オブゞェクトの仮想テヌブルは特定のタむプに関連付けられおいたせん。 タむプの組み合わせごずに固有です。



猫のペットvtable犬のペットvtable
+ ----------------- + + ----------------- +
 | タむプのもの|  | タむプのもの|
+ ----------------- + + ----------------- +
 | ペットのもの|  | ペットのもの|
+ ----------------- + + ----------------- +




同様に、Animal + PetずAnimal + Felineのセットには異なるテヌブルがありたす。぀たり、関数テヌブルは型の䞀意のセットごずに単圢化されたす。



このアプロヌチは、仮想関数テヌブルを構築する問題を完党に排陀したす。仮想化に関䞎しない倀は远加のデヌタを保存せず、Petを実装する任意のタむプで、 1぀たたは別のPet関数がどこにあるかを静的に決定できたす。



この方法の欠点もありたす。倪いポむンタヌは2倍のスペヌスを占有するため、ポむンタヌが倚いず問題になりたす。たた、芁求されたタむプの組み合わせごずに関数テヌブルを単圢化したす。。これは、ある特定の時点で各オブゞェクトのタむプを静的に怜出できるずいう事実ず、同様に特性オブゞェクトのすべおの瞮小により可胜です。ここで単盞化を仮想化に眮き換えるず、パフォヌマンスが倧幅に䜎䞋したす。たた、したがっお、蚀語は型をキャストする胜力が非垞に限られおいたす-箄per。。



泚意障害を芋぀ける機䌚


倪いポむンタヌは、原則ずしお、倪いポむンタヌにさらに䞀般化できたす。 「倪いポむンタヌ」ずしお、Animal + Felineタむプは1぀の共通仮想テヌブルを指したすが、このテヌブルを各特性ごずに別々に2぀に分割しない理由はありたせん。理論的には、これにより、ポむンタヌがさらに倪くなるため、テヌブルの単盞性が制限される可胜性がありたす。このアむデアは定期的に浮䞊したすが、誰も真剣に受け止めたせん。



最埌に、ナヌザヌが単盞むンタヌフェヌスを仮想化できるずいう最近の声明を思い出しおください。これは、「トレむトの実装トレむト」トレむトのむントレむトトレむト、たたはむしろ-トレむトオブゞェクトが独自のトレむトを実装するこずで可胜になりたしたIMHO型システムの最もクヌルな機胜-箄Per。。結果ずしお、このコヌドは有効です。



 //   ... trait Print { fn print(&self); } impl Print for i32 { fn print(&self) { println!("{}", self); } } impl Print for i64 { fn print(&self) { println!("{}", self); } } // ?Sized ,  T    (&, Box, Rc,   ). // Sized (  ) - ,   . //      Traits  [T]  " ". //  `T: ?Sized`,      //    T  ,    , //   Sized    . fn print_it_twice<T: ?Sized + Print>(to_print: &T) { to_print.print(); to_print.print(); } fn main() { //      :  . print_it_twice(&0i32); // 0, 0 print_it_twice(&10i64); // 10, 10 //      i32::Print  i64::Print. let data: [Box<Print>; 2] = [Box::new(20i32), Box::new(30i64)]; for val in &data { //  :    . //   &Box<Print>  &Print ,   //   -     ... print_it_twice(&**val); // 20, 20, 30, 30 } }
      
      





かっこいい。完璧ではありたせんが、楜しいです。残念ながら、Box <Trait>のimpl Traitはありたせん。Box <T>のimpl <TTrait> Traitずうたく盞互䜜甚しないようですが、ただ掘り䞋げおいたせん。たぶん、それは十分だし、その事実T我々サむズの



関連するタむプ



あるタむプによっお䞀般化された䜕かを定矩するずいう事実の結果はどうなりたすかそしお、これで䜕を衚珟したいのでしょうか実際には、衚珟ず結果の䞡方が1぀であるため、私たちが入り蟌んだ型をどのように扱うかを決定したいのです。実際、このタむプは入力パラメヌタヌです。struct Foo <T>は、FooずTから本栌的な型のみを組み立おるこずができるず蚀っおいたす。Foo自䜓は䞍完党です。特別な甚語に情熱がある堎合、型コンストラクタヌであるFoo は、型を匕数ずしお受け取り、結果ずしお型を返す関数であるず蚀えたす。぀たり、高次のタむプです。



次。trait Eat <T>、たたは䞀般化されたtrait Eat-圌は圌自身に぀いお䜕を教えおくれたすか少なくずも、耇数回実装できたす。そしお、その実珟のそれぞれのために念頭に眮いお、サヌドパヌティのタむプの䞀皮で負担するこずが必芁であるTをせず、食べる䞍完党。したがっお、Eatがどこかで実装されおいるず蚀うこずは䞍可胜であるずいう結論は、Eat <T>のみ実装可胜であり、ここでTはナヌザヌによっお決定されたす。



さお、OK、私たちはそれず䜕をしなければなりたせんかこれは、反埩子の䟋で瀺すこずができたす。



 trait Iterator<T> { fn next(&mut self) -> Option<T>; } /// ,    ,    struct StackIter<T> { data: Vec<T>, } //   [min, max) struct RangeIter { min: u32, max: u32, } impl<T> Iterator<T> for StackIter<T> { fn next(&mut self) -> Option<T> { self.data.pop() } } impl Iterator<u32> for RangeIter { fn next(&mut self) -> Option<u32> { if self.min >= self.max { None } else { let res = Some(self.min); self.min += 1; res } } }
      
      





これたでのずころ良い。むンタヌフェむスの䞀般的な実装ずプラむベヌトな実装の䞡方を蚘述できたす。そしお奇跡が起こった-各実数型はIteratorを 䞀床しか実装できない。 StackIter <Cat>はIterator <Cat>のみを実装し、Iterator <Dog>を実装する必芁はありたせん。実際、圌は他の䜕かを実装するこずを蚱可されおいたせん。そうしないず、実装されたタむプのIterator :: nextが返されるオブゞェクトにナヌザヌは困惑したす。



そこで、我々はそれを非垞に喜んでいるTは、入力タむプずしおむテレヌタ、それは入力ず同じタむプだ- TのためのStackIter。それでも、ナヌザヌずしおはIterator :: nextによっお返される型をハヌドコヌディングできないため、これからの脱出はありたせん。この情報は、反埩子を実装する型を提䟛するために必芁です

あたり楜しくないこの瞬間に、関連する型に粟通する時が来たした。



関連付けられた型により、特性の実装が特定の実装に関連付けられた远加の型を瀺す必芁があるこずを瀺すこずができたす。぀たり、特性は特定の関数ず同じ方法で特定のタむプを必芁ずするずいうこずです。適切に再䜜成されたIteratorを次に瀺したす。



 trait Iterator { //       , //      type Item; fn next(&mut self) -> Option<Self::Item>; } /// ,    ,    struct StackIter<T> { data: Vec<T>, } //   [min, max) struct RangeIter { min: u32, max: u32, } impl<T> Iterator for StackIter<T> { //     //   - type Item = T; fn next(&mut self) -> Option<Self::Item> { self.data.pop() } } impl Iterator for RangeIter { //    type Item = u32; fn next(&mut self) -> Option<Self::Item> { if self.min >= self.max { None } else { let res = Some(self.min); self.min += 1; res } } }
      
      





そしお、Iteratorを数回実装するこずはできたせん。関連付けられたタむプは䞀般化できたすが、次のような他のタむプずは別に定矩するこずはできたせん。



 impl<T> Iterator for RangeIter { type Item = T; fn next(&mut self) -> Option<Self::Item> { unimplemented!() } }
      
      





 <anon>:3:6: 3:7 error: the type parameter `T` is not constrained by the impl trait, self type, or predicates [E0207] <anon>:3 impl<T> Iterator for RangeIter { ^
      
      





したがっお、関連付けられた型は圓然発信型ず呌ばれたす。

それで、特性の実装を関連する型に限定したしたが、これは他に䜕を䞎えたすかこれで、以前はアクセスできなかった、そのようなこずを衚珟できたすか

そしお

ステヌトマシンは次のずおりですわずかに調敎された反埩子。



 trait StateMachine { type NextState: StateMachine; fn step(self) -> Option<Self::NextState>; }
      
      





したがっお、これは型であり、そのむンスタンスはstepを実行するように芁求でき、その結果は同じ型の別のむンスタンスに倉換されたす。ゞェネリックで衚珟したす...



 trait StateMachine<NextStep: StateMachine<_____>> { fn step(self) -> Option<NextState>; }
      
      





...そしお、喜んで型の無限再垰を取埗したす。ゞェネリックのゞェネリックタむプはincomingであるため、トレむトのナヌザヌが決定する必芁がありたす。この堎合、このタむプは特性そのものです。それにもかかわらず、関連するタむプがなくおも実行できたす-仮想化がありたす



 trait StateMachine { // Box ! self    Box<Self>. //  Rc<Self>    ,   *Box *. // (    ,     - ..) fn step(self: Box<Self>) -> Option<Box<StateMachine>>; }
      
      





ここでは、関連するタむプなしでのみ、叀い状態マシンを描画したした。元のむンスタンスを吞収したす自己は借甚されたせん。぀たり、ステップの完了埌、それを䜿甚するこずはできなくなりたす-箄Per。。出力では、ステヌトマシンを実装する䜕かを取埗したす。ただし、これが機胜するためには、すべおのステヌトマシンの䜿甚をヒヌプ䞊の実装のみに制限する必芁がありたす。たた、stepの最初の呌び出し埌に特定のタむプのマシンに関する情報も倱われたす。連想型では、1番目も2番目も発生したせん。

そしお、もう1぀ありたす。特性オブゞェクトは、関連する型では機胜したせん。圌らが倀による自己で動䜜しない同じ理由で、具䜓的な実装タむプは䞍明です。友達にする方法は、特定のタむプをすべお指定するこずです。Box <Iterator>は宣誓し、Box <Iterator <Item = u32 >>は非垞にうたく起動したす。



どこ条件



叀い䟋



 impl<T> Generic<T> { // ,   :   `equal`  , //  ,   . // (`x == y`     `y == x`).       , //   `T: Equal<U>`   ?       //   `T`,   `U`   ! //   . fn my_equal<U: Equal<T>>(&self, other: &Generic<U>) -> bool { other.data.equal(&self.data) } }
      
      





これで、型を関連付ける方法がわかりたした。これはどのように圹立ちたすか



 //  :      //   " ",       //   ,    Item! fn min<I: Iterator<Item = T>, T: Ord>(mut iter: I) -> Option<I::Item> { if let Some(first) = iter.next() { let mut min = first; for x in iter { if x < min { min = x; } } Some(min) } else { None } }
      
      





そしお、ここに解決策がありたす-「どこ」条件。

 impl<T> Generic<T> { fn my_equal<U>(&self, other: &Generic<U>) -> bool where T: Equal<U> { self.data.equal(&other.data) } } fn min<I>(mut iter: I) -> Option<I::Item> where I: Iterator, I::Item: Ord, { if let Some(first) = iter.next() { let mut min = first; for x in iter { if x < min { min = x; } } Some(min) } else { None } }
      
      





どこで特性を任意の型に制限できたす。倚かれ少なかれ。これは、眲名で型をむンラむン化するよりも柔軟性がありたす。どこにあなたが特色、特性の定矩、関数、構造䜓、列挙型の実装に固執するこずができたす-倧たかに蚀えば、どこゞェネリック型がありたす。



たた、いく぀かの定矩は、脳を䜜るための動きず陜気になるこずがあり読み取り、誰もが<T>どこMyReference甚に送信IMPL T送信初めおそしおさらに楜しくなりたす。 - prim.perから、どこ圢質ずの盞互䜜甚-オブゞェクトにはいく぀かの特殊効果も䌎いたす。倀によっお自分自身を参照するこずを意味する特性は、特性オブゞェクトでは䜿甚できないこずを芚えおいたすか芁するに、特殊効果はこれを修正できたす



 trait Print { fn print(&self); // `where Self: Sized` ,  //   - . , //   Print  -! fn copy(&self) -> Self where Self: Sized; } impl Print for u32 { fn print(&self) { println!("{}", self); } fn copy(&self) -> Self { *self } } fn main() { let x: Box<Print> = Box::new(0u32); x.print(); }
      
      







高次特性の制限



この時点で屋根がただなくなっおいない堎合は、次のセクションでは屋根を開けないので、幞運です。率盎に蚀っお泥だらけの沌地が発生し、ここで説明されたすが、傷型システムの悪名高い狂信者だけがピッキングを楜しむこずができたす。



次に、高機胜の関数を䜜成したす。これらの関数は、「関数で機胜する関数」です。矎しく有名な䟋はmapです



 let x: Option<u32> = Some(0); let y: Option<bool> = x.map(|v| v > 5);
      
      





5杯のコヌヒヌが戻るず、Rustが特性を介しお行う控えめなコメントに出くわすかもしれたせん。 「ああ、すごい、玠晎らしい特性だ、そうだった」ずあなたは蚀い、すぐに「しかしそれはただの特性だ」ここにありたすFn、FnMutおよびFnOnce。今、私たちにずっおそれらの違いは取るに足りないものであり、私たちは圌らから、私たちが説明したいものにもっず矎しくかかっおいるものを取り䞊げたす。



Fnは実際には特性ではなく、特性のファミリヌです。FnA、B-> Cは、すでに特性です。ゞェネリックのようなもの。そうであっおもこれは䞀般的なものです。FnA、B-> C-盎接利甚できない䞀般化された特性の䞊にある構文糖少なくずもバヌゞョン1.7の堎合。コンパむラはそれを展開したすFN <A、B、C出力=> 。着信タむプ、発信タむプは明確に衚瀺され、すべおが私たちの蚀うずおりです

これに基づいお、mapの䟋のクロヌゞャはFnOnceu32-> boolを実装したす。これたでのずころ、たったく怖くない。これたで。



 fn get_first(input: &(u32, i32)) -> &u32 { &input.0 } fn main() { let a = (0, 1); let b = (2, 3); let x = Some(&a); let y = Some(&b); println!("{}", x.map(get_first).unwrap()); println!("{}", y.map(get_first).unwrap()); }
      
      





実際にどのget_first特性がここに実装されおいたすか同様に>U32 -のFnU32、I32 2行目。



 trait MyFn<Input> { type Output; } // -;    //  ,      . struct Thunk; impl MyFn<&(u32, i32)> for Thunk { type Output = &u32; }
      
      





 <anon>:9:11: 9:22 error: missing lifetime specifier [E0106] <anon>:9 impl MyFn<&(u32, i32)> for Thunk { ^~~~~~~~~~~ <anon>:9:11: 9:22 help: see the detailed explanation for E0106 <anon>:10:19: 10:23 error: missing lifetime specifier [E0106] <anon>:10 type Output = &u32; ^~~~ <anon>:10:19: 10:23 help: see the detailed explanation for E0106 error: aborting due to 2 previous errors
      
      





生涯のない借甚は玔粋な再線です。鉄則借入がタむプの䞀郚である堎合-ラむフタむムを適甚しおくださいもう1぀は、Rustは99のケヌスでこのルヌルに負担をかけないほど賢明であるずいうこずです。デフォルトのラむフタむムを眮き換えるだけです逐語的に翻蚳するのにあたり意味のない別の甚語で、誰もがそれを䜿甚したす-およそPer。。実際のずころ



、get_first、実際にそのようなモンスタヌがありたす



 fn get_first<'a>(input: &'a (u32, i32)) -> &'a u32 { &input.0 }
      
      





別のルヌル関数の眲名を借甚するず、関数はそれらに関しお䞀般化されたす。したがっお、䞀般化された特性を実装する必芁がありたす。



 trait MyFn<Input> { type Output; } struct Thunk; impl<'a> MyFn<&'a (u32, i32)> for Thunk { type Output = &'a u32; }
      
      





コンパむルしたす。そしお今、私はあなたに反察を蚀いたす私たちが必芁ずする特性はFnu32、i32->u32です。そこに、私はあなたに嘘を぀いた。方法ず理由、むテレヌタヌのフィルタヌの䟋を瀺したす。



 ///    struct Filter<I, F> { iter: I, pred: F, } ///   fn filter<I, F>(iter: I, pred: F) -> Filter<I, F> { Filter { iter: iter, pred: pred } } impl<I, F> Iterator for Filter<I, F> where I: Iterator, F: Fn(&I::Item) -> bool, // !   -   !   ? { type Item = I::Item; fn next(&mut self) -> Option<I::Item> { while let Some(val) = self.iter.next() { if (self.pred)(&val) { return Some(val); } } None } } fn main() { let x = vec![1, 2, 3, 4, 5]; for v in filter(x.into_iter(), |v: &i32| *v % 2 == 0) { println!("{}", v); // 2, 4 } }
      
      





芋知らぬ人ず奇劙な。lifetime val を扱うにはpredが必芁です。残念ながら、where 条件をnextに掛けおも、このラむフタむムを明瀺的に指定するこずはできたせん䞀般的に、Iteratorはこれを蚱可したせん。このラむフタむムは、関数内で単に衚瀺および非衚瀺になり、遞択も名前の指定もできたせん。たた、名前を呌び出せない人ず䜜業するにはpredも必芁です私たちは額に登りたす- 人生のすべおの時間で働くためにpredが必芁です。突然



 F: Fn(&I::Item) -> bool
      
      





それは砂糖です

 for<'a> F: Fn(&'a I::Item) -> bool
      
      





぀たり、<'a>はほずんど逐語的に読たれたすfor all' afor all 'a



レッツ・コヌルこの制玄高次の特性HRTBバむンド䞊䜍特性 。耇雑な型構造を持぀ある皮の沌地に既に吞い蟌たれおいない限り、それらを操䜜する必芁はありたせん。通垞、HTRBは機胜特性を操䜜するずきに衚瀺され、そこでも構文糖で芆われおいたす。぀たり、倚くの堎合、ナヌザヌに察しお透過的です。珟時点では、高次特性の制限は有効期間でのみ機胜したす。



高次タむプ



ゞェネリックは本質的に高次の型を衚珟する方法であるこずは既に述べたした。VecはT-> Vec <T>ずいう圢匏の型コンストラクタヌず考えるこずができたす。impl <T> Vec <T>のTraitたたはfn make_vec <T>-> Vec <T>のような汎甚コヌドを蚘述するずきに、この型コンストラクタヌを意味できたす。たた、制限に぀いおも芚えおいたす-型コンストラクタヌに぀いお蚀えば、この発信型を瀺す矩務がありたす。぀たり、コンストラクタ自䜓を䞀般化するこずはできたせん。



たずえば、参照カりントポむンタヌを䜿甚するデヌタ構造が必芁です。 Rustには、RcずArcの 2぀のオプションがありたす。Rc生産的、Arcはスレッドセヌフです。構造の実装の芳点から、これらの型は完党に亀換可胜であるず仮定したす。ただし、構造䜓のナヌザヌにずっおは、どのポむンタヌカりンタヌを䜿甚するかが非垞に重芁です。



もちろん、RcずArcに関しお構造を䞀般化する必芁がありたす。理想的には、次のように曞きたす。



 //   ,   .   ! ///     . /// RefCount   Rc  Arc,    . struct Node<RefCount: RcLike, T> { elem: T, next: Option<RefCount<Node<RefCount, T>>>, }
      
      





くそヌ、それはできたせんナヌザヌはここでRcたたはArcを送信できないため、これはわかりたせん。型は䞀般化されおおり、Rc <SomeType>のように、型匕数をスリップするこずにより完成する必芁がありたす。セットRc <Node <T >>の特性を考え出すこずもできたすが、これはRcずArcを盎接操䜜するよりも消化されにくいです。これは、ロヌンを返すゞェネリックでも次のように䜿甚できたす。



 /// ,    `next` , ///      . trait RefIterator { type Item; fn next(&mut self) -> &mut T }
      
      





既に知っおいるように、それはただの砂糖です

 trait RefIterator { type Item; fn next<'a>(&'a mut self) -> &'a mut Self::Item; }
      
      





ここで厄介な点は、借甚を、型から倖郚に突き出る極端なオブゞェクトずしおハヌドコヌディングしたずいう事実です。぀たり、Self :: Itemをどこかに栌玍する必芁がありたす。喜んでこのロヌンを投げおこの問題を解決したす。



 trait RefIterator { type Item; fn next<'a>(&'a mut self) -> Self::Item<'a>; }
      
      





Self :: Item =mut Tを曞くこずができ、理論的には満足しおいるため、さらに矎しく、䞀般化されおいるように芋えたす。Itemが静かに型コンストラクタヌに倉わり、䞀般化できないこずに気付くたで

コンパむラヌによく尋ねれば、できたす。しかし、私はあなたにそれを䌝えたせんでした。オフィスを撮圱しないでください。

ここでの重芁な知識は、特性には入力タむプず出力タむプがあるこずを理解するこずです。぀たり、特性はタむプの䞊にある関数です。ここを芋おください



 trait TypeToType<Input> { type Output; }
      
      





型コンストラクタ汎甚RefIterリンクカりントむテレヌタを実装したしょう。



 use std::marker::PhantomData; use std::mem; use std::cmp; // ,  RefIter    struct MyType<'a> { slice: &'a mut [u8], index: usize, } //     : trait LifetimeToType<'a> { type Output; } // - , //      /// &'* T struct Ref_<T>(PhantomData<T>); /// &'* mut T struct RefMut_<T>(PhantomData<T>); /// MyType<*> struct MyType_; //   ,      impl<'a, T: 'a> LifetimeToType<'a> for Ref_<T> { type Output = &'a T; } impl<'a, T: 'a> LifetimeToType<'a> for RefMut_<T> { type Output = &'a mut T; } impl<'a> LifetimeToType<'a> for MyType_ { type Output = MyType<'a>; } //   ,   ! // `Self::TypeCtor as LifetimeToType<'a>>::Output` // -   'a  TypeCtor. // // , : <X as Trait>::AssociatedItem  " -", //      . // // :  ,     HRTB, //    `T: 'a`. // `for<'a> Self::TypeCtor: LifetimeToType<'a>`   // `&'a T`    `'a`, //       `T: 'static`! //    "where"  `next`. trait RefIterator { type TypeCtor; fn next<'a>(&'a mut self) -> Option<<Self::TypeCtor as LifetimeToType<'a>>::Output> where Self::TypeCtor: LifetimeToType<'a>; } // ! struct Iter<'a, T: 'a> { slice: &'a [T], } struct IterMut<'a, T: 'a> { slice: &'a mut [T], } struct MyIter<'a> { slice: &'a mut [u8], } // FIXME: https://github.com/rust-lang/rust/issues/31580 //      ,    . //         // (     ,   ) fn _hack_project_ref<'a, T>(v: &'a T) -> <Ref_<T> as LifetimeToType<'a>>::Output { v } fn _hack_project_ref_mut<'a, T>(v: &'a mut T) -> <RefMut_<T> as LifetimeToType<'a>>::Output { v } fn _hack_project_my_type<'a>(v: MyType<'a>) -> <MyType_ as LifetimeToType<'a>>::Output { v } //  (   ) impl<'x, T> RefIterator for Iter<'x, T> { type TypeCtor = Ref_<T>; fn next<'a>(&'a mut self) -> Option<<Self::TypeCtor as LifetimeToType<'a>>::Output> where Self::TypeCtor: LifetimeToType<'a> { if self.slice.is_empty() { None } else { let (l, r) = self.slice.split_at(1); self.slice = r; Some(_hack_project_ref(&l[0])) } } } impl<'x, T> RefIterator for IterMut<'x, T> { type TypeCtor = RefMut_<T>; fn next<'a>(&'a mut self) -> Option<<Self::TypeCtor as LifetimeToType<'a>>::Output> where Self::TypeCtor: LifetimeToType<'a> { if self.slice.is_empty() { None } else { let (l, r) = mem::replace(&mut self.slice, &mut []).split_at_mut(1); self.slice = r; Some(_hack_project_ref_mut(&mut l[0])) } } } impl<'x> RefIterator for MyIter<'x> { type TypeCtor = MyType_; fn next<'a>(&'a mut self) -> Option<<Self::TypeCtor as LifetimeToType<'a>>::Output> where Self::TypeCtor: LifetimeToType<'a> { if self.slice.is_empty() { None } else { let split = cmp::min(self.slice.len(), 5); let (l, r) = mem::replace(&mut self.slice, &mut []).split_at_mut(split); self.slice = r; let my_type = MyType { slice: l, index: split / 2 }; Some(_hack_project_my_type(my_type)) } } } // ! fn main() { let mut data: [u8; 12] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; { let mut iter = Iter { slice: &data }; while let Some(v) = iter.next() { println!("{:?}", v); } } { let mut iter = IterMut { slice: &mut data }; while let Some(v) = iter.next() { println!("{:?}", v); } } { let mut iter = MyIter { slice: &mut data }; while let Some(v) = iter.next() { println!("{:?} {}", v.slice, v.index); } } }
      
      





䞊蚘で䜕が起こっおいるかを解読できるかどうかはわかりたせんが、これが必芁かどうかはわかりたせん。ここで説明した通垞の䞀連のアクションです。䞀般に、Rc / Arcの凊理に圹立ちたすか

そしおはい



 use std::rc::Rc; use std::sync::Arc; use std::ops::Deref; //   T -> Output trait RcLike<T> { type Output; fn new(data: T) -> Self::Output; } //  struct Rc_; struct Arc_; impl<T> RcLike<T> for Rc_ { type Output = Rc<T>; fn new(data: T) -> Self::Output { Rc::new(data) } } impl<T> RcLike<T> for Arc_ { type Output = Arc<T>; fn new(data: T) -> Self::Output { Arc::new(data) } } struct Node<Ref, T> //  `where`     ( - ) where Ref: RcLike<Node<Ref, T>>, { elem: T, //  : Option<Rc<Node<Rc_, T>> next: Option<<Ref as RcLike<Node<Ref, T>>>::Output> } struct List<Ref, T> where Ref: RcLike<Node<Ref, T>>, { head: Option<<Ref as RcLike<Node<Ref, T>>>::Output> } impl<Ref, T, RefNode> List<Ref, T> where Ref: RcLike<Node<Ref, T>, Output=RefNode>, RefNode: Deref<Target=Node<Ref, T>>, RefNode: Clone, { fn new() -> Self { List { head: None } } fn push(&self, elem: T) -> Self { List { head: Some(Ref::new(Node { elem: elem, next: self.head.clone(), })) } } fn tail(&self) -> Self { List { head: self.head.as_ref().and_then(|head| head.next.clone()) } } } fn main() { //  ( ,      ) let list: List<Rc_, u32> = List::new().push(0).push(1).push(2).tail(); println!("{}", list.head.unwrap().elem); // 1 let list: List<Arc_, u32> = List::new().push(10).push(11).push(12).tail(); println!("{}", list.head.unwrap().elem); // 11 }
      
      





私たちはほずんど倉わっが、䞊蚘の条件劚げ句参考RcLike <ノヌド<参考、Tを>>。これは、抜象化の穎です。䞀方で、私たちの構造のナヌザヌはNodeに぀いお絶察に知る必芁はありたせんが、䞀方で、そこに蚀及するこずを匷制されたす。そしお、Refは䜕でもRcLike であるず蚀えたす。技術的には、これは<T> RefRcLike <T>の堎所のように聞こえたす。これにより、Refの䜿甚に関するオプションの配垃の詳现を隠すこずができたす。



ああ。 , , . - , , ! .

( , RFC , , -, — ..)



, , , , , , Rust . , .





同じタむプの2぀のむンスタンスが実際に亀換可胜であるず考えたこずはありたすかそのため、2぀のWidgetがあり、それらを亀換した堎合、誰もリヌドしたせん。ほずんどの堎合、これは倧歓迎です。しかし、同じタむプのむンスタンスの亀換を犁止するずどうなりたすか



たずえば、配列。配列を反埩凊理するずき、通垞は芁玠を取埗するために行われたす。 たあ、このタスクは、アクセス芁玠に異なるニヌズに適したむテレヌタを提䟛するこずが必芁ですむヌタヌ、IterMutずIntoIter。反埩子が私たちに語った堎合、それはより䟿利ではないでしょうどこに芋えるために、私たちは自ら決めたかをそこにホストする



理論的には、配列に独自のむンデックスむテレヌタがあれば可胜です。0、1、2、...、lenの1 - 。むンデックス、ビゞネスで自分自身を実行し、誰もが幞せです。この堎合にのみ、むテレヌタの信頌性が圱響を受けたす。通垞、むテレヌタからは、各芁玠が少なくずも1回トラバヌスされるこずが確実であるこずが期埅され、萜䞋するこずは保蚌されおいたせん。



むンデックスによるアクセスは、䞊蚘のすべおを砎壊したす。むンデックスを眮き換えるこずができ、配列自䜓を倉曎しおむンデックスを無効にするこずができたす。最埌に、ある配列のむンデックスを誀っお別の配列にプルするこずができたす。しかし、これはすべお、さたざたな皋床の努力で解決可胜です。むンデックスの眮換に察しお-それらの型ラッパヌであるため、ナヌザヌは実際の倀を䜿甚できたせん。むンデックスの無効化に察しお、むンデックスを配列の寿呜にリンクするず圹立ちたす。



そしお、配列自䜓の眮換では、どうですか 2぀の配列があり、それらのむンデックスのタむプは同䞀であり、事実䞊亀換可胜ですが、これは望たしくありたせん。私たちを取埗する方法UNが呌ばれたい䞀般化ゞェネ。䞀般化可胜性は、異なる関連型を持぀同じ型の異なるむンスタンスを所有するずいう考えに基づいおいたす。関連付けられた型の倀は、型むンスタンス、぀たりです。



疲れた、ひどく疲れた。ずにかくここでフィニッシュラむンに近づいおいるので、先ほど曞いた䞊蚘のデモをコピヌしたす。ずにかく、すべおがコメントにあるので、読んでください。



 //      , //       ""  //   .        , //    ,     . // (      Vec,        ). // //     "   ",  //   .      //   ,         , //      ( ?).    //   ,      ,    //       ,     . //     .    //       (let idx = arr.validate(idx)). // //     -    //  ,    ,   //       ( moving values in/out  try!). //  ,        //   , ,    ,  . //          //  API ( ,    Vec --     `push`  `pop`. //       . // //     ,        , //       ,        // //      gereeter    //    BTreeMap.  ST Monad  Haskell   . // //      ,     //     &[T]  &mut [T]. fn main() { use indexing::indices; let arr1: &[u32] = &[1, 2, 3, 4, 5]; let arr2: &[u32] = &[10, 20, 30]; //   ( ,     ) indices(arr1, |arr1, it1| { indices(arr2, move |arr2, it2| { for (i, j) in it1.zip(it2) { println!("{} {}", arr1.get(i), arr2.get(j)); //        // println!("{} ", arr2.get(i)); // println!("{} ", arr1.get(j)); } }); }); //   ,      let _a = indices(arr1, |arr, mut it| { let a = it.next().unwrap(); let b = it.next_back().unwrap(); println!("{} {}", arr.get(a), arr.get(b)); // a //   ,    }); //     ,    let (x, y) = indices(arr1, |arr, mut it| { let a = it.next().unwrap(); let b = it.next_back().unwrap(); (arr.get(a), arr.get(b)) }); println!("{} {}", x, y); //  :    !? // (:        ) } mod indexing { use std::marker::PhantomData; use std::ops::Deref; use std::iter::DoubleEndedIterator; // Cell<T>   T;  Cell<&'id _>  `id` . //  ,         // 'id  "" . type Id<'id> = PhantomData<::std::cell::Cell<&'id mut ()>>; pub struct Indexer<'id, Array> { _id: Id<'id>, arr: Array, } pub struct Indices<'id> { _id: Id<'id>, min: usize, max: usize, } #[derive(Copy, Clone)] pub struct Index<'id> { _id: Id<'id>, idx: usize, } impl<'id, 'a> Indexer<'id, &'a [u32]> { pub fn get(&self, idx: Index<'id>) -> &'a u32 { unsafe { self.arr.get_unchecked(idx.idx) } } } impl<'id> Iterator for Indices<'id> { type Item = Index<'id>; fn next(&mut self) -> Option<Self::Item> { if self.min != self.max { self.min += 1; Some(Index { _id: PhantomData, idx: self.min - 1 }) } else { None } } } impl<'id> DoubleEndedIterator for Indices<'id> { fn next_back(&mut self) -> Option<Self::Item> { if self.min != self.max { self.max -= 1; Some(Index { _id: PhantomData, idx: self.max }) } else { None } } } pub fn indices<Array, F, Out>(arr: Array, f: F) -> Out where F: for<'id> FnOnce(Indexer<'id, Array>, Indices<'id>) -> Out, Array: Deref<Target = [u32]>, { //    .      //         (,  //  F).   ,    `indices`  //  ,       . // //          //  ,     `'static`,      ,     //  *this*.           . //       ,    ,  //           // ,     . // //       , //     ,     ,      . //   ,        , //        . let len = arr.len(); let indexer = Indexer { _id: PhantomData, arr: arr }; let indices = Indices { _id: PhantomData, min: 0, max: len }; f(indexer, indices) } }
      
      







ムハハ、そんなに簡単じゃないいいえ、玔粋な地獄。そしお、これはすべお息をのむほど安党でなく、壊れやすいものです。これに察しお、安党でないサりンドを䜿甚するこずに察するRustの䞻匵は、萜䞋する建物に誓わない䞁寧な芁求のようなものです。システムの安定性はすべおのタむプのフィリグリヌフィットおよび寿呜、特に寿呜-箄Per。に䟝存したす最も危険な状態、䞀方、安党でないずマヌクされた行はキャンバス䞊の唯䞀の条件です。



プログラムの䞀般化可胜性ぞの䟝存は、粉末倉庫での喫煙にすぎないので、既存の構文のあたり知られおいない隅からHTRBから少しず぀収集するのではなく、Rustの䞀般化可胜性を盎接サポヌトするこずを本圓に期埅しおいたす。



ずにかく、それで十分です。䜕も曞く力はこれ以䞊ありたせん。すべおがどこにも匕っ匵られたせんでした。蚱しおください。お願いしたす。



All Articles