Rust에서 mutable global variable을 만드는 건 어렵지 않음
단지 unsafe나 동기화를 제공하는 스마트 포인터를 써야 함
Rust는 기본적으로 re-entrant하며 컴파일 타임에 스레드 안전성을 보장하기 때문임
만약 정적 스레드 안전성을 신경 쓰지 않는다면 Zig나 C처럼 쉽게 만들 수 있음
차이는 Rust는 코드의 런타임 동작에 대해 더 많은 보증 도구를 제공한다는 점임
여러 해 Rust를 써본 입장에서, mutable global variable은 “할 수 있다고 해서 해야 하는 건 아니다”의 전형적인 예라고 생각함
다른 언어로 돌아가서 이런 걸 아무렇지 않게 쓰는 걸 보면 안전성 측면에서 미친 짓처럼 느껴짐
“trivial하다, 단지 ~가 필요하다”는 식의 표현은 C++이나 Perl, Haskell에서도 들었던 이야기임
하지만 이런 식의 “단순한 일”들이 쌓이면 결코 단순하지 않게 됨
Rust는 이미 그 선을 넘었고, 이제는 결코 trivial하지 않음
Rust 컴파일러가 스레드 간 race condition을 컴파일 타임에 잡아주는지 궁금함
그렇다면 C보다 매력적일 것 같음
두 변수가 항상 함께 잠겨야 하는 상황에서는 어떻게 처리하는지도 알고 싶음
내가 언어를 만든다면 mutable global variable은 아예 금지시킬 것임
디버깅하다 보면 결국 문제의 근원은 항상 그쪽이었음
Rust의 개념 밀도를 지적한 글에 대해, 실제로는 그 중 5%만 알아도 생산적으로 쓸 수 있다고 생각함
12년 넘게 Rust를 써왔지만 #[fundamental] 같은 건 한 번도 쓸 일이 없었음
Rust에서도 arena allocation을 할 수 있고, allocator 개념도 존재함
기본 allocator가 있을 뿐이며, 보통 Box::new 같은 명시적 힙 할당을 사용함
mutable global은 static FOO: Mutex<T> = Mutex::new(...)처럼 만들 수 있고, 메모리 안전성을 위해 mutex가 필요함
Rust의 타입 시스템은 메모리 안전성뿐 아니라 코드의 의미적 안전성까지 보장하도록 설계되어 있음
하지만 다른 개발자가 다른 5~10%의 개념을 쓸 수 있기 때문에, 협업 시 결국 더 많은 개념을 배워야 함
C에서는 이런 복잡성이 적음 복잡도는 결국 중요한 문제임
“Rust도 arena allocation이 가능하다”는 말은 맞지만, 대부분의 Rust/Go 코드는 작은 단위의 다수의 할당을 기본 경로로 가짐
단순히 가능 여부의 문제가 아니라 기본적인 프로그래밍 스타일의 차이에 대한 이야기임
Rust에서 allocator가 타입이라면, m:n 스레드 모델에서 각 요청마다 별도의 arena를 줄 수 있는지도 궁금함
Rust의 allocator가 전역(global) 인지, 모든 힙 할당이 같은 allocator를 사용하는지도 질문함
Casey Muratori의 batch allocation 영상을 언급하며, 일부 개발자들이 이를 잘못 이해하고 Rust의 RAII를 비판한다고 지적함
Zig Software Foundation이 Asahi Lina의 Rust 관련 발언을 잘못 인용한 사례도 있었음
Zig가 다른 언어를 깎아내리는 마케팅 태도는 별로 마음에 들지 않음
Zig가 마음에 드는 이유는 메모리 고갈을 우아하게 처리할 수 있는 언어이기 때문임
모든 할당이 실패 가능(fallible)하다고 가정하고, 명시적으로 처리해야 함
스택 공간도 마법처럼 다루지 않고, 컴파일러가 호출 그래프를 분석해 최대 크기를 추론함
임베디드 환경에서 이런 자원 중심 설계는 필수적임
하지만 Linux처럼 overcommit을 사용하는 OS에서는 실제로 할당 실패가 발생하지 않음
언어 차원의 처리로는 해결되지 않음
Zig가 존재해야 하는 이유가 Rust가 이미 있는데 무엇이냐는 질문에, 차라리 “C가 있는데 왜 Zig인가?”를 묻겠음
결국 수동 메모리 관리라는 같은 문제를 안고 있음
그렇다면 GC 언어를 쓰는 게 낫다고 생각함
재귀나 함수 포인터 호출이 있을 때 Zig의 스택 크기 추론이 어떻게 작동하는지 궁금함
Zig가 처음은 아니며, 1958년 JOVIAL 이후의 시스템 언어 역사를 살펴볼 필요가 있음
Rust에서도 pre-allocation을 잘 처리할 수 있음
다만 Rust의 표준 라이브러리는 OOM 시 panic을 사용하기 때문에, no-std 환경에서 임베디드 개발을 지원하는 생태계가 따로 있음
Go의 slice는 Rust의 Vec<T>와 다름 append()는 새로운 slice를 반환하며, 기존 메모리를 공유할 수도 있고 아닐 수도 있음
메모리를 줄이는 방법도 없고, append(s, ...)만 쓰면 새 slice를 무시하게 됨
Go는 “내가 말한 대로 해라”는 태도이고, Rust는 “내가 말한 대로 했는지 검증하라”는 태도임
즉, Go는 단순함을 위해 실수를 허용하고, Rust는 복잡해지더라도 실수를 줄이는 방향을 택함
실제로는 slices.Clip으로 메모리를 줄일 수 있음
또한 append(s, ...)만 쓰면 컴파일 오류가 나므로, 원문은 약간 부정확한 주장임
Go는 기능 추가 시 복잡도 증가를 신중히 따지는 언어임
“append(s, …)”는 컴파일조차 되지 않기 때문에 초보자가 그런 실수를 할 수 없다고 생각함
Go에 제네릭이 생겼는데도 List[T] 같은 타입이 널리 쓰이지 않는 게 흥미로움
아마 growable list를 직접 넘길 일이 많지 않기 때문일 것임
Go는 명세와 문서에 대부분의 함정(foot gun) 이 명확히 적혀 있음
단순히 문서를 안 읽고 놀라워하는 경우가 많음
C/C++의 UB(Undefined Behavior) 를 런타임 검사로 잡는 건 현실적으로 어렵다고 생각함
Android도 sanitizer를 모든 커밋에 적용했지만, Rust로 전환하고 나서야 익스플로잇이 줄어듦
Android의 sanitizer 관련 주장에 대한 출처를 요청함
언어 비교 글이 각 언어의 강점과 약점을 솔직하게 다뤄서 좋았음
다만 Raku가 언급되지 않아 아쉬움
내 생각엔 C–Zig–C++–Rust–Go가 저수준 언어의 연속선이라면, 고수준 쪽은 Julia–R–Python–Lua–JS–PHP–Raku–WL로 이어짐
WL이 무엇인지 질문함
Raku는 표현력이 풍부한 범용 언어로, 다중 디스패치·역할(roles)·점진적 타입·게으른 평가·강력한 정규식 시스템을 내장함
문법 정의를 언어 차원에서 지원해 DSL이나 로그 파싱이 쉬움
VM 기반이라 성능은 낮지만, 문제 구조를 직접 표현하기에 적합함
Perl의 후계로서 유연하고 일관된 언어를 지향함
Rust에서 함수가 포인터를 반환하면 힙 할당이 자동으로 일어난다고 생각하는 건 오해임
지역 변수는 스택에 있고, 반환 시 사라지므로 포인터는 무효화됨
Rust는 안전 모드에서는 포인터 역참조가 불가능하고, unsafe 모드에서는 개발자가 유효성 보장 책임을 짐
아마도 Box::new를 “암묵적 할당”으로 착각한 듯함
Go의 escape analysis와 Rust의 명시적 힙 할당을 혼동하는 건 이해하기 어려움
이는 개념을 잘못 이해했거나 의도적으로 오도하는 것처럼 보임
Go의 가장 큰 장점은 간단한 동시성 모델임
goroutine 덕분에 쉽게 병렬 코드를 작성할 수 있음
Go의 장점 중 하나는 코드 일관성 덕분에 대규모 코드베이스를 탐색하기 쉽다는 점임
인터페이스 구현을 찾는 건 어렵지만, 가독성이 높아 팀 협업에 유리함
Go의 error proposal이 흥미로웠음
이런 반복되는 문제를 해결할 수 있는 개선이 나오길 기대함
제네릭은 여전히 어려운 과제일 것임
가장 가까운 대안은 C# 이지만, 여전히 OOP 중심 언어임
글의 전반적인 톤이 신입 개발자의 열정과 호기심이 느껴져 좋았음
Go의 제네릭 부재는 단순한 미니멀리즘이 아니라, 트레이드오프 고민의 결과였다고 생각함
Rust의 lifetime은 많은 사람에게 가장 큰 난관이었고, 언어의 혁신성은 기존 개념의 조합에 있음
Zig의 수동 메모리 관리는 OOP 배제보다는 Data-Oriented Design(DOD) 철학에 기반함
관련 강연: Andrew의 DOD 발표
Russ Cox가 2009년에 “The Generic Dilemma” 글에서 제시한 문제처럼,
“느린 프로그래머, 느린 컴파일러, 느린 실행 중 무엇을 택할 것인가”가 핵심이었음
Go 팀은 결국 이를 만족스럽게 해결하는 절충안을 찾은 것으로 보임
Hacker News 의견
Rust에서 mutable global variable을 만드는 건 어렵지 않음
단지
unsafe나 동기화를 제공하는 스마트 포인터를 써야 함Rust는 기본적으로 re-entrant하며 컴파일 타임에 스레드 안전성을 보장하기 때문임
만약 정적 스레드 안전성을 신경 쓰지 않는다면 Zig나 C처럼 쉽게 만들 수 있음
차이는 Rust는 코드의 런타임 동작에 대해 더 많은 보증 도구를 제공한다는 점임
다른 언어로 돌아가서 이런 걸 아무렇지 않게 쓰는 걸 보면 안전성 측면에서 미친 짓처럼 느껴짐
하지만 이런 식의 “단순한 일”들이 쌓이면 결코 단순하지 않게 됨
Rust는 이미 그 선을 넘었고, 이제는 결코 trivial하지 않음
그렇다면 C보다 매력적일 것 같음
두 변수가 항상 함께 잠겨야 하는 상황에서는 어떻게 처리하는지도 알고 싶음
디버깅하다 보면 결국 문제의 근원은 항상 그쪽이었음
Rust의 개념 밀도를 지적한 글에 대해, 실제로는 그 중 5%만 알아도 생산적으로 쓸 수 있다고 생각함
12년 넘게 Rust를 써왔지만
#[fundamental]같은 건 한 번도 쓸 일이 없었음Rust에서도 arena allocation을 할 수 있고, allocator 개념도 존재함
기본 allocator가 있을 뿐이며, 보통
Box::new같은 명시적 힙 할당을 사용함mutable global은
static FOO: Mutex<T> = Mutex::new(...)처럼 만들 수 있고, 메모리 안전성을 위해 mutex가 필요함Rust의 타입 시스템은 메모리 안전성뿐 아니라 코드의 의미적 안전성까지 보장하도록 설계되어 있음
C에서는 이런 복잡성이 적음
복잡도는 결국 중요한 문제임
단순히 가능 여부의 문제가 아니라 기본적인 프로그래밍 스타일의 차이에 대한 이야기임
Zig Software Foundation이 Asahi Lina의 Rust 관련 발언을 잘못 인용한 사례도 있었음
Zig가 다른 언어를 깎아내리는 마케팅 태도는 별로 마음에 들지 않음
Zig가 마음에 드는 이유는 메모리 고갈을 우아하게 처리할 수 있는 언어이기 때문임
모든 할당이 실패 가능(fallible)하다고 가정하고, 명시적으로 처리해야 함
스택 공간도 마법처럼 다루지 않고, 컴파일러가 호출 그래프를 분석해 최대 크기를 추론함
임베디드 환경에서 이런 자원 중심 설계는 필수적임
언어 차원의 처리로는 해결되지 않음
결국 수동 메모리 관리라는 같은 문제를 안고 있음
그렇다면 GC 언어를 쓰는 게 낫다고 생각함
다만 Rust의 표준 라이브러리는 OOM 시 panic을 사용하기 때문에, no-std 환경에서 임베디드 개발을 지원하는 생태계가 따로 있음
Go의 slice는 Rust의
Vec<T>와 다름append()는 새로운 slice를 반환하며, 기존 메모리를 공유할 수도 있고 아닐 수도 있음메모리를 줄이는 방법도 없고,
append(s, ...)만 쓰면 새 slice를 무시하게 됨Go는 “내가 말한 대로 해라”는 태도이고, Rust는 “내가 말한 대로 했는지 검증하라”는 태도임
즉, Go는 단순함을 위해 실수를 허용하고, Rust는 복잡해지더라도 실수를 줄이는 방향을 택함
또한
append(s, ...)만 쓰면 컴파일 오류가 나므로, 원문은 약간 부정확한 주장임Go는 기능 추가 시 복잡도 증가를 신중히 따지는 언어임
아마 growable list를 직접 넘길 일이 많지 않기 때문일 것임
단순히 문서를 안 읽고 놀라워하는 경우가 많음
C/C++의 UB(Undefined Behavior) 를 런타임 검사로 잡는 건 현실적으로 어렵다고 생각함
Android도 sanitizer를 모든 커밋에 적용했지만, Rust로 전환하고 나서야 익스플로잇이 줄어듦
언어 비교 글이 각 언어의 강점과 약점을 솔직하게 다뤄서 좋았음
다만 Raku가 언급되지 않아 아쉬움
내 생각엔 C–Zig–C++–Rust–Go가 저수준 언어의 연속선이라면, 고수준 쪽은 Julia–R–Python–Lua–JS–PHP–Raku–WL로 이어짐
문법 정의를 언어 차원에서 지원해 DSL이나 로그 파싱이 쉬움
VM 기반이라 성능은 낮지만, 문제 구조를 직접 표현하기에 적합함
Perl의 후계로서 유연하고 일관된 언어를 지향함
Rust에서 함수가 포인터를 반환하면 힙 할당이 자동으로 일어난다고 생각하는 건 오해임
지역 변수는 스택에 있고, 반환 시 사라지므로 포인터는 무효화됨
Rust는 안전 모드에서는 포인터 역참조가 불가능하고, unsafe 모드에서는 개발자가 유효성 보장 책임을 짐
아마도
Box::new를 “암묵적 할당”으로 착각한 듯함이는 개념을 잘못 이해했거나 의도적으로 오도하는 것처럼 보임
Go의 가장 큰 장점은 간단한 동시성 모델임
goroutine 덕분에 쉽게 병렬 코드를 작성할 수 있음
인터페이스 구현을 찾는 건 어렵지만, 가독성이 높아 팀 협업에 유리함
colored function이 없고, 채널 기반 통신이 단순해 정확한 동시성 코드를 빠르게 작성할 수 있음
관련 글: Structured Concurrency or Go Statement Considered Harmful
std.Io인터페이스는 Go의 동시성 모델과 유사함go키워드는std.Io.async, 채널은std.Io.Queue,select는std.Io.select로 대응됨내가 원하는 건 Go의 단순함에 Rust의 결과/에러/열거형 처리와 더 나은 제네릭을 결합한 언어임
OCaml, D, Swift, Nim, Crystal 등을 봤지만 아직 시장을 장악한 언어는 없음
대신 Gleam을 살펴볼 만함
이런 반복되는 문제를 해결할 수 있는 개선이 나오길 기대함
제네릭은 여전히 어려운 과제일 것임
글의 전반적인 톤이 신입 개발자의 열정과 호기심이 느껴져 좋았음
Go의 제네릭 부재는 단순한 미니멀리즘이 아니라, 트레이드오프 고민의 결과였다고 생각함
Rust의 lifetime은 많은 사람에게 가장 큰 난관이었고, 언어의 혁신성은 기존 개념의 조합에 있음
Zig의 수동 메모리 관리는 OOP 배제보다는 Data-Oriented Design(DOD) 철학에 기반함
관련 강연: Andrew의 DOD 발표
“느린 프로그래머, 느린 컴파일러, 느린 실행 중 무엇을 택할 것인가”가 핵심이었음
Go 팀은 결국 이를 만족스럽게 해결하는 절충안을 찾은 것으로 보임