Generic parameters of a trait participate in selecting an implementation, while associated types are determined by the selected implementation. For example, Iterator::Item expresses "what this iterator yields," rather than allowing each next call to arbitrarily select any output type.
Uniqueness is Relative to the Full Set of Implementation Parameters
Iterator has no additional type parameters, so the Item of a concrete iterator type is determined by that implementation. If the trait itself has generic parameters, the implementation must be determined together with those parameters; you cannot generalize this to "with associated types, each Self can only ever have one impl."
Input is the input used to select the implementation, and Output is the result specified by that implementation; the fully qualified syntax clarifies both. The constraint Iterator<Item = u8> requires that the associated type of the selected implementation equals u8. Associated Types
Equality Constraints and Capability Constraints Answer Different Questions
I: Iterator<Item = u8> requires the output to be exactly u8; I: Iterator, I::Item: Display only requires the output to be formattable. The latter interface can accept multiple output types, and the caller does not need to convert them all to String first.
usestd::fmt::Display;fnjoined<I>(input: I)-> String
where
I: IntoIterator,
I::Item: Display,
{letmut output =String::new();for(index, value)in input.into_iter().enumerate(){if index !=0{ output.push(',');}usestd::fmt::Write;write!(&mut output,"{value}").unwrap();}
output
}fnmain(){assert_eq!(joined([1,2,3]),"1,2,3");assert_eq!(joined(["a","b"]),"a,b");assert_eq!(joined(std::iter::empty::<u8>()),"");}
The formatting here is simple concatenation, not CSV serialization: if the input itself contains commas or newlines, the function does not escape them. Associated type constraints describe operations available at compile time; they do not supply data format rules for the algorithm.
Syntax
Relationship Expressed
T: Convert<u8>
Selects the implementation of T for u8
<T as Convert<u8>>::Output
Projects the output type specified by that implementation
T: Convert<u8, Output = u16>
Further requires the output to equal u16
I::Item: Display
Does not fix the output type, only constrains its capabilities
What Can a Standard Iterator Borrow?
std::slice::Iter<'s, T> can implement Item = &'s T, so the statement "Iterators cannot return references" is false. These references are borrowed from the external slice pointed to by the iterator, and the lifetime 's is already part of the iterator type.
The difficulty lies in having the returned item borrow from the current &mut self, rather than a pre-fixed external lifetime. For example, if the iterator internally reuses a read buffer, the next read will overwrite the previous content; or if lending overlapping mutable windows sequentially, you must prevent the caller from holding two windows simultaneously.
Why Standard IterMut Can Hold Two &mut Simultaneously
fnmain(){letmut values =[1,2,3];letmut iter = values.iter_mut();let first = iter.next().unwrap();let second = iter.next().unwrap();*first =10;*second =20;assert_eq!(values,[10,20,3]);}
The two returned references can be used simultaneously because IterMut hands out different elements each time and guarantees that subsequent iterations will not hand out the same position that has already been borrowed. Its Item is &mut T on the external slice's lifetime, not a short borrow of the iterator from this next call. IterMut
Overlapping windows cannot make this guarantee: the first window of length 2 covers indices 0 and 1, the second covers 1 and 2. If both were allowed to write simultaneously, index 1 would have two active exclusive access points. Therefore, the key difference is whether the data overlaps and how long the interface allows the return value to live; it is not "returning a mutable reference requires GAT."
GATs: Associated Type Families That Vary with Borrowing
traitLendingIterator{typeItem<'a>whereSelf:'a;fnnext(&mutself)->Option<Self::Item<'_>>;}structWindows<'s>{data:&'smut [u8],
width:usize,
position:usize,
}impl<'s>Windows<'s>{fnnew(data:&'smut [u8], width:usize)->Self{assert!(width >0);Self{ data, width, position:0}}}implLendingIterator forWindows<'_>{typeItem<'a>=&'amut[u8]whereSelf:'a;fnnext(&mutself)->Option<Self::Item<'_>>{let end =self.position.checked_add(self.width)?;if end >self.data.len(){returnNone;}let start =self.position;self.position +=1;Some(&mutself.data[start..end])}}fnmain(){letmut bytes =[1,2,3];letmut windows =Windows::new(&mut bytes,2);{let first = windows.next().unwrap();
first[1]=9;}{let second = windows.next().unwrap();assert_eq!(second,&[9,3]);
second[0]=8;}assert!(windows.next().is_none());assert_eq!(bytes,[1,8,3]);}
Item<'a> brings the length of the current borrow into the return value. where Self: 'a means that when using this associated type, the borrows involved in Self satisfy that lifetime requirement; it does not require Self to live forever.
If you call windows.next() again while still using first, the compiler will reject the overlapping exclusive borrows. Windows can share underlying positions, but they cannot be accessed simultaneously through two active mutable borrows. This example does not require unsafe.
Consume Item by Item, or Explicitly Take Independent Ownership
If the processing logic only needs to calculate statistics, you can immediately obtain an owned result after each next, and then fetch the next item. If you want to save the complete window, you can clone it into a Vec; this adds allocation and copying, but clearly decouples the result from the borrow of the current window. Collecting window references directly conflicts with the safety goals of overlapping mutable windows.
In the main of the previous example, the braces around first are to clearly demonstrate the order of use; borrows are usually analyzed based on subsequent usage, not necessarily waiting for the lexical scope of the variable to end. If you obtain second first, and then read first, the compiler must consider first still active, and thus reject the second exclusive borrow.
Boundary Input
Behavior of Windows in the Example
Reason
width = 0
Panic during construction
The example explicitly excludes empty window semantics; public interfaces could use Result instead
width greater than data length
First next returns None
No complete window exists
width equals data length
Produces only one window
The next start point cannot accommodate width
Width 2, data length 3
Produces two overlapping windows
The start point increments by 1 each time
Start + width overflows
next returns None
checked_add rejects unrepresentable endpoints
What Does where Self: 'a Constrain?
Expanding the method signature, you can understand it as fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>. Item is no longer a fixed type, but a type family that accepts lifetime parameters; the 'a selected for this call enters the return type, allowing the borrow checker to correlate "still using the window" with "still borrowing the iterator."
For Windows<'s> containing external borrows, Self: 'a requires these borrows to be valid for 'a; you cannot derive longer windows from shorter-lived underlying data. The where clause on the GAT declaration provides preconditions that the implementation can rely on; the example needs to write it because of the borrowing relationship with the method borrowing self. It does not mean "the iterator object must live until the end of the program," nor is it a runtime lifetime check. GATs and Required Where Clauses
This trait is not std::iter::Iterator, so standard iterator combinator methods cannot be automatically applied. GATs expand the expressible interface but do not automatically generate a corresponding adapter ecosystem; you should also consider the current compiler's support boundaries for complex constraints and dyn compatibility.
Generic Usage Does Not Equal Automatic dyn Support
GATs can describe Item<'a>, but lending interfaces with such associated types cannot directly become dyn LendingIterator based on this. The standard approach of dyn Iterator<Item = u8> fixing a single output type does not automatically solve an entire type family that varies with lifetimes. When dynamic boundaries are needed, consider putting processing actions inside trait methods, or returning owned data; the choice depends on whether copying is allowed and what compositional capabilities the caller needs.
Complex consumers may also need for<'a> higher-ranked lifetime constraints. This means satisfying conditions for all quantified lifetimes; you cannot mechanically substitute it with a single named lifetime or 'static. When encountering compiler diagnostics, first narrow down to the actual trait, where clauses, and compiler version verification; do not blindly add 'static to borrowed data just to eliminate errors.
Why cannot overlapping windows be retained together?
First see how width-three windows share elements, then move the second window until disjoint. The lending interface still blocks borrowing self again while the old output remains needed: its signature ties output to that borrow, rather than proving arbitrary window arithmetic at each call. Ordinary IterMut instead guarantees disjoint outputs through its implementation.
Preparing the visual
Why cannot overlapping windows be retained together?
A lending output is tied to this &mut self; retaining it for later use blocks another next even when selected ranges happen not to overlap.
{"id":"rust-lending-window","title":"Why cannot overlapping windows be retained together?","summary":"A lending output is tied to this &mut self; retaining it for later use blocks another next even when selected ranges happen not to overlap.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"control\"><label for=\"start\" data-i18n=\"start\"></label><output for=\"start\" id=\"start-value\"></output><input id=\"start\" type=\"range\" min=\"1\" max=\"3\" value=\"1\" step=\"1\"></div><label class=\"check\"><input type=\"checkbox\" id=\"hold\" checked><span data-i18n=\"hold\"></span></label><div class=\"track-row\"><span data-i18n=\"old\"></span><div id=\"old\" class=\"track\"></div></div><div class=\"track-row\"><span data-i18n=\"new\"></span><div id=\"new\" class=\"track\"></div></div><div class=\"metrics\"><div><span data-i18n=\"overlap\"></span><strong id=\"overlap\"></strong></div><div><span data-i18n=\"allowed\"></span><strong id=\"allowed\"></strong></div></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"ok\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"bad\"></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)));function draw(){let s=n('start'),hold=$('#hold').checked;set('start-value',s);cells('old',Array.from({length:6},(_,i)=>({text:i,cls:i<3?'on':'empty'})));cells('new',Array.from({length:6},(_,i)=>({text:i,cls:i>=s&&i<s+3?'gold':'empty'})));set('overlap',Math.max(0,3-s));set('allowed',hold?'×':'✓');set('explanation',t(hold?'bad':'ok'));}inputs(draw);","audio":false,"strings":{"allowed":"Another next","bad":"The old output still borrows &mut self, blocking next. Copying into Vec creates independently owned data.","heading":"Why cannot overlapping windows be retained together?","hold":"Use the old window afterward","intro":"Move the second window; compare disjoint iterator elements with windows borrowing self.","new":"New window [start, start+3)","ok":"The old output is no longer used; end its borrow before obtaining the next window.","old":"Old window [0, 3)","overlap":"Shared elements","reset":"Reset","scope":"Uses the article’s Windows signature. The range slider explains potential overlap without changing its borrow contract.","scopeLabel":"Model scope and assumptions","start":"Second window start"}}
Return-Position impl Trait: Hiding a Concrete Type
The behavior of the two branches is determined by the bool captured by the closure, and the returned hidden type remains the same. If you return Range on one side of an if and Filter on the other, they will not automatically unify just because they both implement Iterator; you need a unified structure, an explicit enum, or dyn to erase the differences. impl Trait
The hidden type can depend on the function's generic parameters, and lifetime capture rules also vary by edition, so the statement "there is only one global type for all calls" is also an oversimplification. The trait constraints promised by the interface are capabilities the caller can rely on, not the exposure of underlying iterator composition names.
Using the Same Structure to Accommodate Different Runtime Choices
The previous selected puts the choice into the closure state, so both calls have the same return type. Another common case is "possibly no elements": you can turn the branch into an Option, and then return the same IntoIter.
If you change the true branch to return std::iter::once(7) and the false branch to return std::iter::empty(), although they have the same Item, they are different concrete types and cannot directly become the same return-position impl Iterator. This limitation is unrelated to the number of elements; the same type can completely produce different numbers of elements at runtime.
Connection with Async Traits
GATs can express associated Future types with borrows, making the interface explicitly state the relationship between the return value and &self. However, this does not mean that stable Rust can arbitrarily write impl Future in every associated type definition to name anonymous async blocks.
Native async fn in trait and return-position impl Trait in trait have been stabilized in Rust 1.75 and can be used for corresponding static calls; they also do not automatically make the trait usable as dyn. When dynamic erasure of Future is needed, common interfaces return Pin<Box<dyn Future<Output = T> + 'a>>, and additionally decide whether to require Send. For actual async mechanisms, see async/await, and for object limitations, see trait objects.