Rustでの借入ず生涯

Yahoo!の゚ンゞニアであるArthur Liaoのブログの蚘事「Rust Borrow and Lifetimes」の翻蚳を玹介したす。



Rustは、 バヌゞョン1.0から掻発に開発されおいる新しいプログラミング蚀語です。 Rustずそれがなぜクヌルなのかに぀いお別のブログを曞くこずができたすが、今日は借甚システムず自分自身を含む倚くの新人を混乱させる寿呜に焊点を圓おたす。 この投皿では、Rustの基本的な理解があるこずを前提ずしおいたす。 そうでない堎合は、最初にガむド自䜓ず道案内ガむドを読むこずができたす。



リ゜ヌス所有暩ず借入



Rustは、掗緎された借甚システムを䜿甚するこずにより、ガベヌゞコレクションなしでメモリセキュリティを提䟛したす。 リ゜ヌスを解攟しおいるリ゜ヌスには、少なくずも1人の所有者がいたす。 たたはmutを䜿甚しおリ゜ヌスにアクセスするための新しいバむンダヌを䜜成できたす。これは、借甚および可倉借甚ず呌ばれたす。 コンパむラは、すべおの所有者ず借り手の適切な動䜜を監芖したす。



コピヌしお移動



借甚システムに移行する前に、Rustでcopyおよびmoveメ゜ッドがどのように凊理されるかを知る必芁がありたす。 StackOverflowからのこの回答は、読むだけで十分です。 䞀般に、割り圓おず関数では、次のように呌び出されたす。



1.倀がコピヌされる堎合プリミティブ型のみが関䞎し、メモリやファむル凊理などのリ゜ヌスが関䞎しない堎合、コンパむラヌはデフォルトでコピヌしたす。

2.それ以倖の堎合、コンパむラは所有暩を転送転送し、元のバむンディングを無効にしたす。



芁するに、ポッド叀いデヌタの読み取り=>コピヌ、非ポッド線圢タむプ=>移動。



参考のために、远加の泚意事項がいく぀かありたす。



* Rustコピヌ方法はCに䌌おいたす。 倀ごずの䜿甚は、セマンティックコピヌたたはクロヌンではなく、バむトコピヌシャドりmemcpyコピヌメ゜ッドです。

*ポッド構造をコピヌ䞍可にするには、NoCopyマヌカヌフィヌルドを䜿甚するか、ドロップ特性を実装したす。



移動埌、所有暩は次の所有者に移されたす。



リ゜ヌスリリヌス



Rustでは、次の堎合など、所有暩がなくなるずすぐにオブゞェクトが解攟されたす。



1.所有者ぱリアの倖にいるか、

2.バむンディングの所有暩が倉曎されたすそれにより、元のバむンディングは無効になりたす



所有者借り手ず借り手借り手の特暩ず制限



このセクションは、特暩の芳点からコピヌず移動の方法を参照したRustハンドブックに基づいおいたす。



所有者にはいく぀かの特暩がありたす。 そしお倚分



1.リ゜ヌス割り圓おを監芖する

2.リ゜ヌスを倉曎せずに借りる耇数の借り入れたたは倉曎可胜排他的で、

3.所有暩の移転移転を䌎う。



所有者にはいく぀かの制限もありたす。



1.借甚のプロセスでは、所有者はaリ゜ヌスを倉曎したり、b倉曎された圢匏で借甚するこずはできたせん。

2.可倉借入のプロセスでは、所有者はaリ゜ヌスにアクセスするこずもb借りるこずもできたせん。



借り手にはいく぀かの特暩もありたす。 借り手は、アクセスの取埗たたは借りたリ゜ヌスの倉曎に加えお、別の借り手ず共有するこずもできたす。



1.借甚者は、ポむンタ䞍倉の借甚を配垃コピヌできたす。

2.倉動借入人は倉動借入を譲枡移動できたす。 可倉参照が移動されおいるこずに泚意しおください。



コヌド䟋



十分な話。 いく぀かのコヌドを芋おみたしょうplay.rust-lang.orgでRustコヌドを実行できたす。 次のすべおの䟋では、「struct Foo」を䜿甚したす。これは、パックされた動的に割り圓おられた倀を含むためコピヌできないFoo構造です。 コピヌ䞍可胜なリ゜ヌスを䜿甚するず、操䜜の可胜性が制限されたす。これは、調査段階では良い考えです。



サンプルコヌドごずに、所有者、借り手などの領域を瀺すために「地域グラフ」も瀺されおいたす。タむトルバヌの波括匧は、コヌド自䜓の䞭括匧ず䞀臎しおいたす。



所有者は、倉数借入のプロセスでリ゜ヌスにアクセスできたせん


「println」の最埌の行のコメントを倖さない限り、次のコヌドはコンパむルされたせん。



struct Foo { f: Box<int>, } fn main() { let mut a = Foo { f: box 0 }; //   let x = &mut a; // :    `af`  , . . `a`     // println!("{}", af); } { ax * }  a |_____|  x |___| x = &mut a  af | 
      
      





これは所有者の制限2aに違反したす。 ネストされたブロックに「let x =mut a;」を入れるず、println行の前で借甚が終了したす。 これはうたくいくかもしれたせん

 fn main() { let mut a = Foo { f: box 0 }; { //   let x = &mut a; //     } println!("{}", af); } { a { x } * }  a |_________|  x |_| x = &mut a  af | OK
      
      





借り手は倉数の借り入れを新しい借り手に移すこずができたす


このコヌドは、借り手2の特暩を瀺しおいたす。倉数借り手xは、倉数借り入れを新しい借り手yに転送移動できたす。



 fn main() { let mut a = Foo { f: box 0 }; //   let x = &mut a; //       y let y = x; // :   : `xf` // println!("{}", xf); } { axy * }  a |_______|  x |_| x = &mut a  y |___| y = x  xf | 
      
      





移動埌、元の借り手xは借りたリ゜ヌスにアクセスできなくなりたす。



借入゚リア



ここにリンクずmutを枡すずすべおが面癜くなり、倚くの初心者が混乱したす。



生涯





借入の党歎史においお、借り手による借り入れはどこで始たり、どこで終わるかを知るこずが重芁です。 ラむフタむムガむドでは、これをラむフタむムず呌びたす。



ラむフタむムは、ポむンタヌが有効な実行距離の静的な掚定倀です。垞に、プログラム内の匏たたはブロックに察応したす。



=借甚



借甚に぀いおのいく぀かの蚀葉。 たず、=借甚、およびmut =可倉借甚であるこずを思い出しおください。 蚘号が衚瀺されるずころはどこでも、これは借甚です。



次に、蚘号が各構造䜓フィヌルド内たたは関数/クロヌゞャヌ戻り倀型たたはキャプチャヌされたリンクに衚瀺される堎合、そのような構造䜓/関数/クロヌゞャヌは借甚者であり、すべおの借甚芏則が適甚されたす。



第䞉に、借入ごずに所有者ず単䞀の借り手たたは耇数の借り手がいたす。



ロヌン゚リアの拡倧



ロヌンの分野に぀いお䞀蚀。 たず、ロヌン地域

借入が有効な地域であり、

-借り手はロヌンの範囲を拡倧できたす以䞋を参照。



第二に、借り手は、割り圓おたたは関数呌び出しで発生するコピヌ䞍倉の借入たたは移動䞍倉のロヌンを通じおロヌンの範囲を拡倧できたす。 受信者これは、新しい接続、構造、機胜、たたはクロヌゞャである堎合がありたすが新しい借り手になりたす。



第䞉に、ロヌン地域はすべおの借り手の゚リアの結合であり、借りたリ゜ヌスはロヌン地域党䜓で有効でなければなりたせん。



ロヌン匏



これで、ロヌンの公匏ができたした。

リ゜ヌス領域> =ロヌン領域=すべおの借り手の領域の結合



コヌド䟋



ロヌンの範囲を拡倧するいく぀かの䟋を芋おみたしょう。 struct Foo構造は以前ず同じです。



 fn main() { let mut a = Foo { f: Box::new(0) }; let y: &Foo; if false { //  let x = &a; //     y,    y = x; } // :     `af`, ,    // af = Box::new(1); } { a { xy } * }  a |___________|  x |___| x = &a  y |_____| y = x   |=======|  af | 
      
      





ロヌンがifブロック内で発生し、借り手xがifブロックを超えたずしおも、圌は割り圓おy = x;を介しお借り入れの範囲を拡倧したため、2぀の借り手xずyがありたす。 ロヌンの匏によるず、ロヌンの領域は借り手xず借り手yの結合であり、最初のロヌンlet x =a; 本䜓の最埌たで。 let yFoo;バむンディングは借り手ではないこずに泚意しおください



条件は垞にfalseであるため、ifブロックは決しお満たされないこずに気付いたかもしれたせんが、コンパむラはリ゜ヌス `a`の所有者がリ゜ヌスにアクセスするこずを䟝然ずしお防ぎたす。 これは、すべおのロヌンチェックがコンパむル時に行われ、実行時には䜕もしないためです。



耇数のリ゜ヌスを借りる



これたでのずころ、1぀のリ゜ヌスからの借甚のみに焊点を圓おおきたした。 借り手は耇数のリ゜ヌスを取るこずができたすか もちろん たずえば、関数は2぀のリンクを取埗し、特定の基準に応じお1぀を返すこずができたす。たずえば、どちらのリンクが他のリンクよりも倧きいかなどです。



 fn max(x: &Foo, y: &Foo) -> &Foo
      
      





max関数はポむンタヌを返すため、借り手です。 返される結果はむンバりンドリンクのいずれかになる可胜性があるため、2぀のリ゜ヌスを借甚したす。



名前付きロヌン゚リア



入力ずしお耇数のポむンタヌがある堎合、ラむフタむムガむドで定矩された名前で、ラむフタむムを䜿甚しおそれらの関係を瀺す必芁がありたす。 しかし、今のずころは、それらを名前付きロヌン゚リアず呌びたしょう。



䞊蚘のコヌドは、借り手間の関係を瀺すこずなしにコンパむラヌによっお受け入れられたせん。぀たり、借り手、぀たりロヌン領域にグルヌプ化された借り手です。 この実装は正しいでしょう



 fn max<'a>(x: &'a Foo, y: &'a Foo) -> &'a Foo { if xf > yf { x } else { y } } (        'a'.) max( { } )  *x <-------------->  *y <-------------->   'a <==============>  x |___|  y |___|   |___|   
      
      





この関数には、1぀のロヌン領域「a」ず3぀の借り手がありたす。2぀の入力パラメヌタヌず、関数によっお返される結果です。 䞊蚘の借入匏は匕き続き適甚されたすが、今ではすべおの借入リ゜ヌスが匏を満たす必芁がありたす。 以䞋の䟋を参照しおください。



コヌド䟋



次のコヌドでmax関数を䜿甚しお、aずbから最も倚くを遞択しおみたしょう。



 fn main() { let a = Foo { f: Box::new(1) }; let y: &Foo; if false { let b = Foo { f: Box::new(0) }; let x = max(&a, &b); // : `b`     // y = x; } } { a { bx ( ) y } }  a |________________|   b |__________|    |==========|   |_| &a   |_| &b  x |________| x = max(&a, &b)  y |___| y = x
      
      





let x = maxa、b;の前に aおよびbは匏でのみ有効な䞀時参照であり、3番目の借り手xはifブロックの最埌たで2぀のリ゜ヌス䞡方ずも借り手チェックがある堎合はaたたはbのいずれかを䜿甚するため、すべおが順調です。ロヌンはlet x = maxa、b; ifブロックの最埌たで。 リ゜ヌスaおよびbは、ロヌン領域党䜓で有効であるため、ロヌンの匏を満たしたす。



ここで、最埌の倀y = x;のコメントを倖すず、yが4番目の借り手になり、メむンブロックの最埌たでロヌン領域が増加し、その結果、リ゜ヌスbがロヌン蚈算匏テストに倱敗したす。



借り手ずしおの構造



関数ずクロヌゞャに加えお、構造はその領域で耇数の参照を維持しながら、いく぀かのリ゜ヌスを占有するこずもできたす。 以䞋の䟋ず、ロヌンの匏がどのように適甚されるかを芋おみたしょう。 Link構造を䜿甚しおリンクを保存したしょう䞍倉の借甚



 struct Link<'a> { link: &'a Foo, }
      
      





構造はいく぀かのリ゜ヌスを借りたす



フィヌルドが1぀だけの堎合でも、リンク構造には耇数のリ゜ヌスを䜿甚できたす。



 fn main() { let a = Foo { f: Box::new(0) }; let mut x = Link { link: &a }; if false { let b = Foo { f: Box::new(1) }; // : `b`     // x.link = &b; } } { ax { b * } }  a |___________|   b |___|    |=========|  x |_________| x.link = &a  x |___| x.link = &b
      
      





䞊蚘の䟋では、借り手xは所有者aからリ゜ヌスを借りおおり、貞し出し゚リアはメむンブロックの最埌たで行きたす。 すべお順調です。 最埌の行x.link =b;のコメントを倖すず、xは所有者bからリ゜ヌスを借甚しようずし、リ゜ヌスbはロヌン蚈算匏テストに倱敗したす。



返り倀なしでロヌン領域を拡匵する機胜



戻り倀のない関数は、入力パラメヌタヌを介しおロヌンの範囲を拡匵するこずもできたす。 たずえば、store_foo関数はLinkぞの可倉リンクを受け入れ、Foo䞍倉な借甚リンクをそこに保存したす。



 fn store_foo<'a>(x: &mut Link<'a>, y: &'a Foo) { x.link = y; }
      
      





次のコヌドでは、借甚したリ゜ヌスがリ゜ヌスを所有しおいたす。 リンク構造は盞互に借り手xを参照したす぀たり、* xは借り手です。 ロヌン゚リアはメむンブロックの最埌たで行きたす。



 fn main() { let a = Foo { f: Box::new(0) }; let x = &mut Link { link: &a }; if false { let b = Foo { f: Box::new(1) }; // store_foo(x, &b); } } { ax { b * } }  a |___________|   b |___|    |=========|  *x |_________| x.link = &a  *x |___| x.link = &b
      
      





最埌の行のコメントを解陀する堎合store_foox、b; この関数は、bをx.linkに保存しようずしたす。リ゜ヌスbの領域はロヌン領域党䜓をカバヌしおいないため、リ゜ヌスbを別の借入リ゜ヌスにし、ロヌン蚈算匏のテストに倱敗したす。



耇数のロヌン゚リア



関数には、いく぀かの名前付きロヌン領域がありたす。 䟋

 fn superstore_foo<'a, 'b>(x: &mut Link<'a>, y: &'a Foo, x2: &mut Link<'b>, y2: &'b Foo) { x.link = y; x2.link = y2; }
      
      





このおそらくあたり有甚ではない機胜には、2぀の異なるロヌン領域が含たれたす。 各ロヌン地域には、独自の借入匏がありたす。



生涯が混乱する理由



最埌に、Rustのロヌンシステムで䜿甚される存続期間ずいう甚語が玛らわしいず思う理由を説明したすしたがっお、このブログではこの甚語の䜿甚を避けおいたす。



ロヌンに぀いお話すずき、「生涯」には3぀のタむプがありたす。



Aリ゜ヌスの所有者の寿呜たたは所有/借甚したリ゜ヌス

Bロヌン党䜓の「存続期間」、぀たり 最初のロヌンから返品たで

C個々の借り手たたは借りたポむンタの寿呜



誰かが「存圚時間」ずいう甚語を話すずき、圌は䞊蚘のいずれかを意味するかもしれたせん。 耇数のリ゜ヌスず借り手が関䞎しおいる堎合、すべおがさらに混乱したす。 たずえば、関数たたは構造䜓の宣蚀で「名前付きラむフタむム」は䜕をしたすか これはA、B、たたはCを意味したすか



以前のmax関数では



 fn max<'a>(x: &'a Foo, y: &'a Foo) -> &'a Foo { if xf > yf { x } else { y } }
      
      





「a」の存続期間ずはどういう意味ですか 2぀のリ゜ヌスが関係し、存圚時間が異なるため、これはAであっおはなりたせん。 x、y、および関数の戻り倀の3぀の借り手があり、それらはすべお異なるラむフタむムを持っおいるため、Cにするこずはできたせん。 これはBを意味したすか おそらく。 しかし、ロヌンの党領域は特定のオブゞェクトではありたせん。どのようにしお「存圚時間」を持぀こずができたすか それを存圚の時間ず呌ぶのは間違っおいたす。



これは、借りたリ゜ヌスのラむフタむムの最小芁件を意味するず蚀うかもしれたせん。 堎合によっおは、これが違いを生む可胜性がありたすが、どのようにしおそれらを「存圚時間」ず呌ぶこずができたすか



所有暩/借入の抂念自䜓は耇雑です。 「ラむフタむム」を䞎えるものをめぐる混乱は、開発をさらに理解しにくいものにしおいるず思いたす。



PS䞊蚘で定矩したA、B、およびCを䜿甚するず、ロヌンの匏は次のようになりたす。



  A >= B = C1 U C2 U 
 U Cn
      
      





Rustを孊ぶのはあなたの䟡倀がありたす



ロヌンず習埗を孊ぶには長い時間がかかりたすが、勉匷するのは面癜いです。 Rustはガベヌゞコレクションなしでセキュリティを達成しようずしたすが、それでも非垞にうたく機胜したす。 Haskellをマスタヌするず、プログラムの方法が倉わるず蚀う人もいたす。 Rustを開発するこずもあなたの時間の䟡倀があるず思いたす。



この投皿がお圹に立おば幞いです。



All Articles