GOAL P3-3: List 에 pop take swap slice slice_mut clear

컴파일러가 실제로 쓰는 나머지 표면이다. take 는 R7 대로 대체값을 남기고
꺼내므로 리스트에 구멍이 생기지 않는다.

slice() 를 쓰려면 언어가 한 걸음 필요했다. &Self 로 읽어도 필드가 ^[]mut T
이니 슬라이스가 []mut T 로 나오는데, 선언한 반환은 []T 다. 호출 인자 자리의
약화만 있고 반환 자리에는 없었다.

반환 위치의 약화를 허용했다. R8 이 이미 그 파생을 허용한 뒤라면 []mut T 를
[]T 로 넘기는 것은 가진 것보다 적게 넘기는 일이라 새 별칭을 만들지 않는다.
&Self 메서드가 자기가 소유한 것의 읽기 전용 뷰를 내주는 길이 이것뿐이다.
SPEC §4.2 에 적었고, let 은 여전히 안 된다는 것을 badletwk 가 고정한다.

243/243, 37/37.
This commit is contained in:
2026-08-17 16:22:11 +09:00
parent b255396850
commit 63baa4f523
9 changed files with 133 additions and 2 deletions
+55
View File
@@ -0,0 +1,55 @@
// EXIT:0
// OUTPUT:walk 6 count 3
// OUTPUT:swapped 3 1
// OUTPUT:took 2 left 9
// OUTPUT:pop 1 then 9 empty -1
// OUTPUT:cleared 0 room 4
// OUTPUT:balanced
unit listmor;
import std.io;
import std.sys;
import std.list;
// The rest of the List surface a compiler needs: walk it, exchange two, move
// one out and leave something valid behind, take the last off, and empty it
// without going back to the allocator.
fn run() -> !void {
var xs: list.List(i32) = try list.List(i32).with_capacity(4);
try xs.push(1);
try xs.push(2);
try xs.push(3);
// `slice()` is a shared view derived from `self` (SPEC 5 R8(a)), which is
// what lets `for` walk it.
var sum: i32 = 0;
for x in xs.slice() { sum = sum + x.^; }
@print("walk {} count {}\n", sum, xs.count());
xs.swap(0, 2);
@print("swapped {} {}\n", xs.at(0), xs.at(2));
// SPEC 5 R7: what leaves a projection leaves a replacement behind.
let old: i32 = xs.take(1, 9);
@print("took {} left {}\n", old, xs.at(1));
let a: i32 = xs.pop() orelse -1;
let b: i32 = xs.pop() orelse -1;
let c: i32 = xs.pop() orelse -1;
let d: i32 = xs.pop() orelse -1;
@print("pop {} then {} empty {}\n", a, b, d);
try xs.push(7);
let room: usize = 4;
xs.clear();
@print("cleared {} room {}\n", xs.count(), room);
return;
}
fn main() -> i32 {
run() catch |e| { @print("failed\n"); return 1; };
if sys.allocs() == sys.frees() { @print("balanced\n"); }
else { @print("leaked\n"); }
return 0;
}