noreturn
noreturn (절대 반환하지 않는 타입)
noreturn은 프로그램의 흐름이 정상적으로는 끝나지 않는 코드, 즉 값을 돌려주지 않고 흐름을 끊어버리는 것들의 타입이에요. break나 return 같은 것들이 결코 "계속 진행"하지 않는 걸 타입으로 드러낸다고 생각하면 돼요. 어떤 것들이 noreturn 타입인지 바로 살펴볼게요.
본문
noreturn은 다음 항목들의 타입이에요:
breakcontinuereturnunreachablewhile (true) {}
if 절이나 switch prong처럼 여러 타입을 함께 맞춰야 할 때, noreturn 타입은 어떤 타입과도 호환돼요. 예를 들어 살펴볼게요.
fn foo(condition: bool, b: u32) void {
const a = if (condition) b else return;
_ = a;
@panic("do something with a");
}
test "noreturn" {
foo(false, 1);
}
$ zig test test_noreturn.zig
1/1 test_noreturn.test.noreturn...OK
All 1 tests passed.
위 코드에서 if (condition) b else return을 보면, condition이 참이면 a는 b가 돼요. 그런데 조건이 거짓이면 return이 실행되는데, return은 noreturn 타입이라 값을 만들지 않아요. 그런데도 이 if 식은 b(u32)와 return(noreturn)을 함께 맞춰서 결과적으로 u32 타입으로 잘 성립해요. return 때문에 코드가 실제로는 그 지점에서 끝나버리기 때문에, 타입 검사 단계에서 아무 문제 없이 받아들이는 거죠.
noreturn의 또 다른 쓰임새는 exit 함수예요. 프로세스를 종료하는 함수는 절대 값을 돌려주지 않으니까, 반환 타입이 noreturn으로 선언돼요.
const std = @import("std");
const expectEqual = std.testing.expectEqual;
extern "kernel32" fn ExitProcess(exit_code: c_uint) callconv(.winapi) noreturn;
test "foo" {
const value = bar() catch ExitProcess(1);
try expectEqual(1234, value);
}
fn bar() anyerror!u32 {
return 1234;
}
$ zig test test_noreturn_from_exit.zig -target x86_64-windows --test-no-exec
여기서 bar() catch ExitProcess(1)은 에러가 나면 ExitProcess(1)를 호출해서 프로세스를 바로 종료해요. ExitProcess가 noreturn이라 에러가 났을 때는 프로그램이 그대로 끝나버리고, 정상 경로에서만 value가 채워져요. 이렇게 noreturn을 활용하면 "이 경로로는 흐름이 계속되지 않는다"는 의도를 컴파일러와 코드를 읽는 사람 모두에게 명확하게 전달할 수 있어요.
더 알아보기
unreachable이나while (true) {}같은noreturn표현이 실전에서 어떻게 쓰이는지는 메뉴얼의 unreachable 섹션에서 더 볼 수 있어요.- 함수가 이런
noreturn타입을 반환 타입으로 쓰는 방식은 메뉴얼의 Functions 섹션에서 이어서 확인할 수 있어요.