これらの型はまず、値を存続させる責任を誰が負うか、そして最終的に誰が解放するかを決定します。Box は単一の所有者を表現し、Rc と Arc は複数の共有所有者を表現します。内部値の修正が必要かどうか、スレッド間で同期する必要があるかどうかは、別の問題のグループです。
型 所有権 クローンの意味 典型的な用途
Box 独占 T: Clone の場合、内部値を新しい Box にクローン 再帰構造、DST の所有、独立した割り当て
Rc 共有、非アトミックカウント 強い参照を増やすだけで、T はクローンしない スレッド単一の共有構造
Arc 共有、アトミックカウント 強い参照を増やすだけで、T はクローンしない スレッドの制約を満たす共有所有権
Weak T を存続させない 弱い参照を増やす 逆方向のリンク、所有権を持たない観察者
Box は再帰的なサイズを間接参照に変える
ノードが直接次の同型のノードを含んでいる場合、そのサイズは自身に再帰的に依存します。Box を使って次のノードを間接的に保持することで、現在のノードのサイズを確定できます。Box や Box<[T]> は、コンパイル時に具体的なサイズが不明な値を所有することもできます。
Sized な T に対して、通常の Box は単一ポインタによる表現を保証しますが、ポインタの幅はターゲットプラットフォームに依存します。DST のポインタは対応するメタデータも携带しており、一律に「8 バイト」とは呼びません。ゼロサイズ型の Box は、T のために非ゼロのヒープ領域を確保する必要はありません。Box
Box::new(large_array) は、ヒープ上の値を最終的に所有することを表現していますが、構築過程で大配列のスタック上の一時オブジェクトが絶対に生成されないことを保証するものではありません。大きなバッファは Vec から構築してから into_boxed_slice すべきです。ソースコードにポインタが 1 つあるからといって、ピーク時のスタック使用量を推論しないでください。
fn main ( ) {
let bytes: Box< [ u8 ] > = vec! [ 0 ; 1_000_000 ] .into_boxed_slice ( ) ;
assert_eq! ( bytes.len ( ) , 1_000_000 ) ;
let moved = bytes; assert_eq! ( moved[ 0 ] , 0 ) ;
}
Box 自体は移動できますが、内部の値が固定されているわけではありません。有効な &mut T を通じて、T を置き換えたり移出したりできます。アドレス固定の契約が必要な場合は Pin に関係します。メモリレイアウト を参照してください。
Box の移動と Box 内の値の移動
fn main ( ) {
let boxed = Box :: new( String :: from( " owned" ) ) ;
let address = ( & * boxed) as * const String ;
let moved = boxed;
assert_eq! ( ( & * moved) as * const String , address) ;
let value: String = * moved;
assert_eq! ( value, " owned" ) ;
}
Box のハンドルを移動しても、ヒープ上の String オブジェクトは元の位置に残っています。その後 *moved によって String の所有権を取得し、Box を消費します。String が所有するバイトバッファと、ヒープ上の String オブジェクト自体は分けて考える必要があります。ハンドル移動の前後でアドレスが同じだからといって、T が永遠に移動不可能である証明にはならず、Pin の契約に代わるものではありません。
再帰構造では破棄パスも考慮する必要があります。Box は型のサイズの無限再帰を解決しますが、長いチェーンにおける逐層の Drop 呼び出しの深さを自動的に解消するわけではありません。非常に長いリストでは、明示的なイテレーションによる解体が必要になる場合があります。連続した要素のみであれば、Vec はレイアウトと走査の面でより直接的な選択肢となることが多いです。
Rc と Arc が共有するのは同じ値
Rc::clone(&a) と Arc::clone(&a) は新しい所有権ハンドルを作成しますが、通常 T: Clone は要求されません。値とカウントの配置は実装の詳細であり、あるプラットフォーム上のフィールドのバイト数を安定した ABI として描画すべきではありません。
Rc は Send でも Sync でもありません。Arc のカウントはスレッド間で調整できますが、デフォルトのアロケータを持つ Arc において、Send と Sync を実現するには依然として T: Send + Sync が必要です。Arc<RefCell> は、自動的にスレッドセーフな内部借用を得るわけではありません。Arc
Arc のアトミックカウントのコストは、アーキテクチャ、競合、キャッシュの影響を受けます。「Rc より 10 倍遅い」という一般的な結論は出せません。頻繁なクローンが重要かどうかは、実際のホットパスで測定すべきです。借用 &T で十分であれば、毎回所有者を作成する必要はありません。
値の等価性と同一の共有オブジェクト
use std:: rc:: Rc;
fn main ( ) {
let first = Rc:: new( String :: from( " same" ) ) ;
let shared = Rc:: clone( & first) ;
let independent = Rc:: new( String :: from( " same" ) ) ;
assert_eq! ( first, independent) ; assert! ( Rc:: ptr_eq( & first, & shared) ) ;
assert! ( ! Rc:: ptr_eq( & first, & independent) ) ;
assert_eq! ( Rc:: strong_count( & first) , 2 ) ;
drop ( shared) ;
assert_eq! ( Rc:: strong_count( & first) , 1 ) ;
}
Rc::clone は所有権ハンドルを明示的に複製します。(*first).clone() は String の Clone を呼び出します。後者は別の String を生成しますが、一般的な型の Clone が必ずしも到達可能なデータをすべて深コピーするとは限りません。例えば Arc フィールドを持つ構造体は、そのフィールドを依然として共有する可能性があります。したがって、「T をクローンした」からといって「完全に隔離されたオブジェクトグラフが得られる」わけではありません。
関数が呼び出し中のみデータを読み取る場合、&T を受け取るだけで十分です。共有所有権を保持する必要がある場合にのみ、Rc/Arc を受け取るかクローンして、対応する契約を表現します。すべての引数を Arc に変更すると、それを必要としないインターフェースに割り当てと共有戦略が伝播してしまいます。
Weak:値を存続させないが、安全に所有権の取得を試みられる
少なくとも 1 つの強い参照 T は存続;Weak は upgrade して強い参照を取得できる 1 最後の強い参照が解放される T をデストラクト;既存の Weak の upgrade は None を返す 2 最後の Weak も解放される 参照カウントが使用していた残りの割り当てを回収可能 3 値のライフサイクルと参照カウント割り当てのライフサイクル 通常の Rc/Arc のライフサイクルの例;Weak は T を存続させず、生ポインタにも等しくない。
use std:: cell:: Cell;
use std:: rc:: Rc;
struct Tracked ( Rc< Cell< usize > > ) ;
impl Drop for Tracked {
fn drop ( & mut self ) { self .0. set ( self .0. get ( ) + 1 ) ; }
}
fn main ( ) {
let drops = Rc:: new( Cell:: new( 0 ) ) ;
let first = Rc:: new( Tracked( Rc:: clone( & drops) ) ) ;
let second = Rc:: clone( & first) ;
let observer = Rc:: downgrade( & first) ;
drop ( first) ;
assert! ( observer.upgrade ( ) .is_some ( ) ) ;
assert_eq! ( drops.get ( ) , 0 ) ;
drop ( second) ;
assert_eq! ( drops.get ( ) , 1 ) ;
assert! ( observer.upgrade ( ) .is_none ( ) ) ;
}
upgrade が成功して返された新しい強い参照は、その存続期間中 T を存続させます。失敗した場合は None を返し、解参照可能なダングリングポインタを露出させることはありません。最後の強い参照が解放された時点で T はすでにデストラクトされており、Weak があっても背後の割り当ては保持され続けます。
一般的なツリーモデルでは、親ノードが子ノードを強い参照で持ち、子ノードが親ノードを Weak で参照します。重要なのは、どの方向を必ず弱くする固定ルールがあることではなく、所有権グラフ内で不要なノードが強い参照のループによって互いに存続させられないようにすることです。親から子への強い参照のみではループは形成されません。双方向が強い参照である場合にのみ、ループが形成される可能性があります。
逆方向のリンクでは、対象がすでに消えていることを許容する
use std:: cell:: RefCell;
use std:: rc:: { Rc, Weak} ;
struct Node {
name : & 'static str ,
parent : RefCell< Weak< Node> > ,
children : RefCell< Vec< Rc< Node> > > ,
}
impl Node {
fn new ( name : & 'static str ) -> Rc< Self > {
Rc:: new( Self {
name, parent: RefCell:: new( Weak:: new( ) ) ,
children: RefCell:: new( Vec :: new( ) ) ,
} )
}
}
fn main ( ) {
let child = Node:: new( " child" ) ;
{
let parent = Node:: new( " parent" ) ;
* child.parent.borrow_mut ( ) = Rc:: downgrade( & parent) ;
parent.children.borrow_mut ( ) .push ( Rc:: clone( & child) ) ;
let owner = child.parent.borrow ( ) .upgrade ( ) .unwrap ( ) ;
assert_eq! ( owner.name, " parent" ) ;
}
assert! ( child.parent.borrow ( ) .upgrade ( ) .is_none ( ) ) ;
assert_eq! ( child.name, " child" ) ;
}
child には外部の強い所有者があるため、parent が破棄されても存続し続けます。しかし、その親へのリンクはもはや upgrade できません。これは Weak が許容する正常な状態であり、呼び出し側は None を処理する必要があります。この例は 1 回の接続のみを示しています。完全なツリー API では、古い親ノードのリストの維持や、祖先が子孫にならないようにするなどの構造不変条件を維持する必要があり、Weak 自体がこれらのビジネスルールを検証することはありません。
Weak は自動クリーンアップされるキャッシュでもありません。HashMap の値がすべて Weak の場合、対象が消えた後もキーと弱いハンドルが表に残り続ける可能性があります。upgrade が失敗した場合は削除するか、定期的に掃除する必要があります。存続期間とキャッシュの容量は異なる問題です。
値の破棄と割り当て解放は別の時点
Weak を作ってから強参照をすべて解放すると、T は消えても Weak 用の割り当ては残ります。この時 upgrade は None になり、最後の Weak の解放で割り当ても回収できます。強参照が残る時の upgrade 成功は、新たな強所有者を増やします。
図を準備しています 再読み込み
値の破棄と割り当て解放は別の時点 最後の強参照で T は破棄され、残る Weak は割り当てを保持しますが T を復活できません。
{"id":"rust-weak-lifetime","title":"値の破棄と割り当て解放は別の時点","summary":"最後の強参照で T は破棄され、残る Weak は割り当てを保持しますが T を復活できません。","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"actions\"><button id=\"strongAdd\" data-i18n=\"strongAdd\"></button><button id=\"strongDrop\" data-i18n=\"strongDrop\"></button><button id=\"weakAdd\" data-i18n=\"weakAdd\"></button><button id=\"weakDrop\" data-i18n=\"weakDrop\"></button><button id=\"upgrade\" data-i18n=\"upgrade\"></button><button id=\"reset\" data-i18n=\"reset\"></button></div><div class=\"metrics\"><div><span data-i18n=\"strong\"></span><strong id=\"strong\"></strong></div><div><span data-i18n=\"weak\"></span><strong id=\"weak\"></strong></div></div><div class=\"two\"><section class=\"card\"><header data-i18n=\"value\"></header><div id=\"value\" class=\"cell\"></div></section><section class=\"card\"><header data-i18n=\"allocation\"></header><div id=\"allocation\" class=\"cell\"></div></section></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"initial\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"success\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"fail\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"changed\"></p></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:550;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:550 20px ui-monospace,monospace}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:ui-monospace,monospace;font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px ui-monospace,monospace;background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n/* Action labels must remain readable across locales and narrow embeds. */\n.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}\n.actions button{overflow-wrap:anywhere;white-space:normal;min-height:58px;padding:8px}\nbutton{overflow-wrap:anywhere}\n@media(min-width:640px){.actions{grid-template-columns:repeat(3,minmax(0,1fr))}}\n\n.metrics span{overflow-wrap:anywhere}\n.panel{border:1px solid var(--rule);border-radius:9px;padding:12px;margin:14px 0;min-width:0}.panel h3{font-size:13px;margin:0 0 10px;color:var(--muted)}.source{font:13px/1.8 ui-monospace,monospace;white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0}.source mark{background:var(--accent-soft);color:var(--ink);border-bottom:2px solid var(--accent)}.source .fault{background:var(--second-soft);border-color:var(--second)}.diagram{display:block;width:100%;height:auto;max-height:320px;margin:14px 0;overflow:visible}.diagram text{fill:var(--ink);font:12px ui-monospace,monospace}.diagram .edge{stroke:var(--muted);stroke-width:1.5;fill:none}.diagram .edge.on{stroke:var(--accent);stroke-width:3}.diagram .node{fill:var(--surface);stroke:var(--rule);stroke-width:1.5}.diagram .node.on{fill:var(--accent-soft);stroke:var(--accent)}.diagram .node.gold{fill:var(--second-soft);stroke:var(--second)}.log{font:12px/1.8 ui-monospace,monospace;white-space:pre-line;overflow-wrap:anywhere;min-height:7.2em}.table{display:grid;gap:5px;margin:14px 0}.table .tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.table .tr>*{padding:8px 5px;background:var(--surface);border-radius:4px;min-width:0;overflow-wrap:anywhere;font-size:12px}.table .tr .on{background:var(--accent-soft)}.table .tr .gold{background:var(--second-soft)}.choices button{min-height:44px}.metrics strong{min-height:1.6em}.code-line{display:block}.code-line.on{background:var(--accent-soft);border-left:2px solid var(--accent);padding-left:6px}.status-badge{font-size:12px;color:var(--accent)}.tile-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.tile-grid .cell{min-height:40px}.actions button{min-height:48px}.form-field{display:grid;gap:8px;margin:14px 0;font-size:13px}.form-field input{width:100%;font-family:ui-monospace,monospace}.metrics.three{grid-template-columns:repeat(3,minmax(0,1fr))}.formula{min-height:3.2em}.presets{flex-wrap:wrap}.presets button{flex-basis:25%;min-height:44px}\n\n.budget{display:flex;height:28px;margin:16px 0;background:var(--surface);border-radius:5px;overflow:hidden}.budget i{display:block;background:var(--accent);transition:width .2s}.budget i.gold{background:var(--second)}.intervals{margin:16px 0}.interval{position:relative;height:36px;background:var(--surface);margin:6px 0;border-radius:4px;overflow:hidden}.interval i{position:absolute;height:100%;background:var(--accent-soft);border-left:2px solid var(--accent)}.interval i.gold{background:var(--second-soft);border-color:var(--second)}.interval span{position:relative;z-index:1;font:12px/36px ui-monospace,monospace;padding-left:7px}.table .tr>*{min-height:4.8em}.formula{white-space:pre-line;min-height:7.2em}.actions:has(>button:only-child){grid-template-columns:1fr}.metrics strong{min-height:3.2em}.rangegrid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:3px;margin:14px 0}.rangegrid .cell{min-height:30px;padding:3px 1px;font-size:10px}.message{min-height:7.2em;white-space:pre-line;font:13px/1.8 ui-monospace,monospace;overflow-wrap:anywhere}\n/* Allow CJK hanging punctuation without overflowing a metric's border box. */\n.metrics>div{padding-right:4px}.metrics span{line-break:anywhere}\n\n.chart-wrap{margin:18px 0}.chart-label{font-size:12px;color:var(--muted);min-height:3.2em}.curve{display:block;width:100%;height:auto;max-height:320px;overflow:visible}.curve text{font:11px ui-monospace,monospace;fill:var(--muted)}.gridline{stroke:var(--rule);stroke-width:1}.metrics strong{font-size:20px;min-height:3.2em}.formula{font:12px/1.8 ui-monospace,monospace;min-height:9em}.readout{min-height:8em}.presets button{flex-basis:30%}.scope p{overflow-wrap:anywhere} .unit-note{font-size:12px;color:var(--muted);margin:10px 0}\n\n.metrics span{word-break:break-all;line-break:strict}.metrics>div{padding-right:6px}\n\n.cell{min-height:72px}.metrics strong{min-height:3.6em}\n\n ","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}const esc=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));const fmt=s=>'{'+[...s].sort().join(', ')+'}';function graph(id,nodes,edges){const el=$('#'+id);el.setAttribute('viewBox','0 0 360 200');el.innerHTML='<defs><marker id=\"arrow-'+id+'\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto-start-reverse\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"var(--muted)\"/></marker></defs>'+edges.map(e=>{const a=nodes[e[0]],b=nodes[e[1]],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1;if(edges.some(r=>r[0]===e[1]&&r[1]===e[0])){const nx=-dy/d,ny=dx/d;return '<path class=\"edge '+(e[2]?'on':'')+'\" d=\"M '+(a.x+dx/d*22+nx*12)+' '+(a.y+dy/d*22+ny*12)+' Q '+((a.x+b.x)/2+nx*38)+' '+((a.y+b.y)/2+ny*38)+' '+(b.x-dx/d*25+nx*12)+' '+(b.y-dy/d*25+ny*12)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}return '<line class=\"edge '+(e[2]?'on':'')+'\" x1=\"'+(a.x+dx/d*25)+'\" y1=\"'+(a.y+dy/d*25)+'\" x2=\"'+(b.x-dx/d*28)+'\" y2=\"'+(b.y-dy/d*28)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}).join('')+nodes.map(n=>'<g><circle class=\"node '+(n.cls||'')+'\" cx=\"'+n.x+'\" cy=\"'+n.y+'\" r=\"25\"/><text x=\"'+n.x+'\" y=\"'+(n.y+4)+'\" text-anchor=\"middle\">'+esc(n.name)+'</text>'+(n.sub?'<text x=\"'+n.x+'\" y=\"'+(n.y+43)+'\" text-anchor=\"middle\">'+esc(n.sub)+'</text>':'')+'</g>').join('');}function table(id,rows){$('#'+id).classList.add('table');$('#'+id).replaceChildren(...rows.map(r=>{const row=document.createElement('div');row.className='tr';row.style.gridTemplateColumns='repeat('+r.length+',minmax(0,1fr))';r.forEach(x=>{const c=document.createElement('div');if(typeof x==='object'){c.textContent=x.text;c.className=x.cls||'';}else c.textContent=x;row.append(c)});return row;}));}function band(id,parts,total){$('#'+id).replaceChildren(...parts.map((p,i)=>{const e=document.createElement('i');e.style.width=(100*p.value/total)+'%';e.className=i%2?'gold':'on';e.title=p.name+': '+p.value;return e}));}function intervals(id,items,total){$('#'+id).replaceChildren(...items.map(p=>{const row=document.createElement('div');row.className='interval';const bar=document.createElement('i');bar.style.left=(100*p.start/total)+'%';bar.style.width=(100*(p.end-p.start)/total)+'%';bar.className=p.cls||'';const label=document.createElement('span');label.textContent=p.label;row.append(bar,label);return row;}));}function plot(id,series,xr,yr,opts={}){const e=$('#'+id),X=x=>42+(x-xr[0])/(xr[1]-xr[0])*298,Y=y=>175-(y-yr[0])/(yr[1]-yr[0])*148;let out='<defs><clipPath id=\"clip-'+id+'\"><rect x=\"42\" y=\"27\" width=\"298\" height=\"148\"/></clipPath></defs>';for(let i=0;i<3;i++){let x=xr[0]+i*(xr[1]-xr[0])/2,y=yr[0]+i*(yr[1]-yr[0])/2;out+='<path class=\"gridline\" d=\"M '+X(x)+' 27V175M42 '+Y(y)+'H340\"/><text x=\"'+X(x)+'\" y=\"194\" text-anchor=\"middle\">'+esc(opts.xfmt?opts.xfmt(x):Number(x.toFixed(2)))+'</text><text x=\"36\" y=\"'+(Y(y)+4)+'\" text-anchor=\"end\">'+esc(opts.yfmt?opts.yfmt(y):Number(y.toFixed(2)))+'</text>';}out+='<g clip-path=\"url(#clip-'+id+')\">';for(const s of series){out+='<path fill=\"none\" stroke=\"'+(s.color||'var(--accent)')+'\" stroke-width=\"2.5\" '+(s.dash?'stroke-dasharray=\"5 4\"':'')+' d=\"'+s.pts.map((p,i)=>(i?'L':'M')+X(p[0]).toFixed(2)+','+Y(p[1]).toFixed(2)).join(' ')+'\"/>';}for(const p of opts.points||[])out+='<circle cx=\"'+X(p[0])+'\" cy=\"'+Y(p[1])+'\" r=\"4\" fill=\"var(--second)\" stroke=\"var(--paper)\" stroke-width=\"1.5\"/>';if(opts.cursor!==undefined)out+='<path d=\"M'+X(opts.cursor)+' 27V175\" stroke=\"var(--muted)\" stroke-dasharray=\"3 3\"/>';e.innerHTML=out+'</g>';e.dataset.curves=JSON.stringify(series.map(s=>s.pts));}const curve=(fn,lo,hi,n=120)=>Array.from({length:n+1},(_,i)=>{const x=lo+(hi-lo)*i/n;return[x,fn(x)]});function inputs(draw){document.querySelectorAll('input,select').forEach(e=>{e.addEventListener('input',draw);e.addEventListener('change',draw)});draw()}const n=id=>+$('#'+id).value;const show=(id,v,d=3)=>set(id,Number(v.toFixed(d)));let s=1,w=0,msg='initial';function draw(){set('strong',s);set('weak',w);set('value',t(s?'alive':'gone'));set('allocation',t(s||w?'kept':'gone'));$('#value').className='cell '+(s?'on':'empty');$('#allocation').className='cell '+(s||w?'gold':'empty');$('#strongAdd').disabled=!s||s>=8;$('#strongDrop').disabled=!s;$('#weakAdd').disabled=!s||w>=8;$('#weakDrop').disabled=!w;$('#upgrade').disabled=!w||s>=8;set('explanation',t(msg));}for(let id of ['strongAdd','strongDrop','weakAdd','weakDrop','upgrade','reset'])$('#'+id).onclick=()=>{msg='changed';if(id==='strongAdd')s++;if(id==='strongDrop')s--;if(id==='weakAdd')w++;if(id==='weakDrop')w--;if(id==='upgrade'){msg=s?'success':'fail';if(s)s++;}if(id==='reset'){s=1;w=0;msg='initial'}draw()};draw();","audio":false,"strings":{"alive":"生存","allocation":"制御用の割り当て","changed":"ハンドル数を更新しました。値と割り当てを別々に確認してください。","fail":"upgrade は None です。Weak は破棄済みの T を復活できません。","gone":"解放済み","heading":"値の破棄と割り当て解放は別の時点","initial":"Rc::new(T):強参照が 1 個。","intro":"強参照・弱参照を増減し、Weak::upgrade を試します。","kept":"保持","reset":"リセット","scope":"単一スレッドの Rc の論理数です。weak は利用者の Weak 数で、内部の暗黙カウントを含みません。","scopeLabel":"モデルの範囲と前提","strong":"強参照数","strongAdd":"強参照を複製","strongDrop":"強参照を解放","success":"upgrade は Some を返し、強参照を 1 個増やします。","upgrade":"upgrade を試す","value":"T の生存","weak":"弱参照数","weakAdd":"弱参照を作る","weakDrop":"弱参照を解放"}}
共有値の修正:他の所有者にも変更を反映させるかどうかを明確にする
要件 方法 結果
すでに一意に所有しており、直接修正する get_mut クローンしない;他の強い参照や Weak が存在する場合は None を返す
自分のバージョンを修正する make_mut 複数の強い参照がある場合、T をクローンし、古い所有者は依然として古い値を見る
所有者全員が見る状態を修正する RefCell、Mutex などの内部可変性 共有オブジェクトを維持し、アクセスを別途制約する
最後の強い所有者を消費して T を取り出す try_unwrap 強い参照数が 1 であればよく、既存の Weak は成功を妨げない
Rc/Arc の make_mut において、強い参照は 1 つだが Weak も存在する場合、これらの Weak は新しい値との関連付けから外れます。T をクローンする必要はありませんが、Weak はその後、修正された値への upgrade を行えません。これは get_mut が直接拒否する動作とは異なります。Rc::make_mut
use std:: rc:: Rc;
fn main ( ) {
let mut current = Rc:: new( String :: from( " v1" ) ) ;
let snapshot = Rc:: clone( & current) ;
Rc:: make_mut( & mut current) .push_str ( " +edit" ) ;
assert_eq! ( & * snapshot, " v1" ) ;
assert_eq! ( & * current, " v1+edit" ) ;
let weak = Rc:: downgrade( & current) ;
assert! ( Rc:: get_mut( & mut current) .is_none ( ) ) ;
Rc:: make_mut( & mut current) .push ( ' !' ) ;
assert! ( weak.upgrade ( ) .is_none ( ) ) ;
let another_weak = Rc:: downgrade( & current) ;
assert_eq! ( Rc:: try_unwrap( current) .unwrap ( ) , " v1+edit!" ) ;
assert! ( another_weak.upgrade ( ) .is_none ( ) ) ;
}
強い参照が 1 つのみ かつ Weak が存在しない 複数の強い参照が存在 他の所有者は旧バージョンを保持 強い参照 1 つ + Weak 弱い観察者は T を存続させない 直接 &mut T を取得 T はクローンしない T を複製し、複製側を変更 他の所有者は元の値を保持 既存の Weak から分離 T を移し、旧 Weak を切り離す make_mut:現在の所有関係に基づいてコピーするかどうかを決定 これは make_mut の動作の分岐です;get_mut は呼び出し側に代わってクローンしたり Weak から分離したりしません。
コピーオンライトは必ずしも深いスナップショットではない
use std:: cell:: Cell;
use std:: rc:: Rc;
# [ derive ( Clone ) ]
struct Version {
title : String,
counter : Rc< Cell< u32 > > ,
}
fn main ( ) {
let mut current = Rc:: new( Version {
title: String :: from( " v1" ) , counter: Rc:: new( Cell:: new( 0 ) ) ,
} ) ;
let old = Rc:: clone( & current) ;
Rc:: make_mut( & mut current) .title.push_str ( " edited" ) ;
current.counter.set ( 7 ) ;
assert_eq! ( old.title, " v1" ) ;
assert_eq! ( current.title, " v1 edited" ) ;
assert_eq! ( old.counter.get ( ) , 7 ) ;
assert! ( Rc:: ptr_eq( & old.counter, & current.counter) ) ;
assert! ( ! Rc:: ptr_eq( & old, & current) ) ;
}
外側の Version は分離されましたが、派生した Clone は counter に対して Rc をコピーするだけで、2 つのバージョンは依然として同じカウンターを見ます。製品上のセマンティクスで完全な不変スナップショットが要求される場合、フィールド自体が不変であることを保証するか、各層のデータを明示的にクローンする必要があります。外側に Rc を被せて make_mut を呼び出すだけでは、この保証を確立するには不十分です。
T の取得に失敗した場合、元の所有権を保持し続ける
use std:: rc:: Rc;
fn main ( ) {
let first = Rc:: new( String :: from( " keep me" ) ) ;
let second = Rc:: clone( & first) ;
let first = Rc:: try_unwrap( first) .expect_err ( " まだ別の強い所有者がある" ) ;
assert! ( Rc:: ptr_eq( & first, & second) ) ;
drop ( second) ;
assert_eq! ( Rc:: try_unwrap( first) .unwrap ( ) , " keep me" ) ;
}
try_unwrap が失敗して Err を返した場合、元のハンドルが呼び出し側に返されるため、共有オブジェクトを捨てなければならないわけではなく、保持し続けることができます。Arc の並列シナリオではさらに注意が必要です。複数のスレッドがそれぞれ try_unwrap(...).ok() を実行しても、誰も T を取得できず、各自が Err を破棄し、最終的にデストラクタが T を解放することがあります。すべての強い所有者が「ちょうど 1 つが値を取り出す」というプロトコルに参加することを希望する場合、Arc::into_inner の対応する保証を調べ、各強いハンドルがプロトコルに従うことを確保してください。Arc::into_inner
Arc::strong_count を先にチェックしてから unsafe で &mut T を作成しないでください。カウントの照会と他のスレッドの操作の間で状態が変化する可能性があり、Weak も関係してきます。get_mut などの用意された契約を使用してください。内部可変性の具体的な選択については、Cell、RefCell と Mutex を参照してください。