Rust 부트스트래핑과 언어 선택에 대한 주장에 반박하며, Rust·Go·OCaml·Zig·C·Hare의 설계와 생태계를 비교하는 글입니다.
"미니멀리스트" 소프트웨어, 즉 터미널에서 실행되는 무엇이든 작성한다면, 이런 "비대화"를 인지하고, 의존성이 덜 많고 리소스 소모가 덜한 언어를 사용하는 가능성을 고려하거나, Rust의 더 가벼운 "gcc" 버전을 기다려 주세요. 감사합니다!
당신은 초점이 DragonflyBSD라고 말합니다. 내가 결코 사용하지 않는 "이상한" 플랫폼을 더 쉽게 지원할 수 있게 하려고, 컴파일 타임 보장이 더 약하고, 의존성에 대한 "겁없는 업그레이드"에 대한 집중도도 낮고, CLI 도구에서 우회해야 할 런타임 시작 시간도 더 길고, 그리고/또는 내가 실제로 신경 쓰는 플랫폼들에 대한 배포도 더 어려운 도구와 생태계를 사용함으로써 내가 감수해야 하는 추가 시간과 스트레스에 대해 당신이 돈을 지불해 줄 건가요?
(TL;DR: 저 링크는 "이상한 아키텍처는 애초에 지원되지 않았다"라는 제목의 블로그 글로, 당신이 결코 그 정도의 지원을 제공할 의도가 없었던 플랫폼으로 누군가가 당신의 프로젝트를 포팅할 때, 업스트림에 무엇을 기대하는 것이 합리적인지를 그것이 바꾸지 않는다는 내용입니다.)
...그리고 기록을 위해 말하자면, 나는 OPNsense 라우터와 NearlyFreeSpeech.NET 공유 웹 호스팅을 통해 FreeBSD를 사용하긴 합니다... 다만 DragonflyBSD는 아닙니다... 그리고 네, 인정하건대 Linux에서 그것들을 위해 Rust 물건을 크로스 컴파일하는 가장 번거롭지 않은 방법이 Vagrant가 관리하는 VM인 것처럼 보이지 않았으면 좋겠습니다.
OCaml은 비슷하게 복잡하다
OCaml은 자기 자신을 호스팅하는, 기계어로 컴파일하는 툴체인이 아니며, 최적화 스택도 GCC나 LLVM과 경쟁하는 수준이 아닙니다. 사과와 오렌지를 비교하고 있는 겁니다.
사실, 당신의 전체 컴파일 시간 차트에서 사과 대 사과 비교에 조금이라도 가까운 유일한 것은 GCC뿐이며, 이것도 스택에서의 특권적 역할 때문에 완전히 사과 대 사과는 아닙니다.
도대체 "Pest"와 "SnakeMake"는 뭐야??? 하하, 적어도 Brainfuck 10줄은 있네!
https://pest.rs/는 Rust에서 두 번째로 오래되었고 두 번째로 잘 알려진 파서 생성기입니다. (Yacc/Bison을 떠올리면 됩니다. Nom이 아마 1위이고, LALRPOP이 아마 3위를 차지할 겁니다.)
적어도 그 질문에 답하려고 시도하는 인상이라도 주지 않으면 Rust 개발자들은 더더욱 "음, 저 사람들은 분명 내 관점에서 보려고 전혀 노력하지 않네. 내가 왜 저 사람들이 생각하는 걸 신경 써야 하지?"라는 방향으로 밀려날 가능성이 큽니다.
이것은 PEG 파싱을 기반으로 하기 때문에 자체 DSL을 갖고 있으며, Yacc/Bison 같은 LR(1) 파싱 기반이 아닙니다. (Nom은 Rust 소스 코드 안에 정의된 파서 컴비네이터를 사용하고, Pest는 PEG를 사용하며, LALRPOP은 Yacc/Bison처럼 LR(1)을 사용합니다.)
솔직히 말해, 나는 SnakeMake를 들어본 적도 없고 Rust가 왜 그걸 사용할지도 모르겠습니다. 그래서 Brainfuck이 있다는 점을 보면, 두 항목 모두 cloc이 다른 무언가를 잘못 식별한 것일 가능성을 시사합니다. 파일명 기반이 아닌 어떤 종류의 탐지를 한다면, YAML 파일 몇 개를 Snakemake 파일로 오인하는 건 충분히 가능해 보입니다.
다른 언어들은 어떻게 스스로를 부트스트랩하는가
당신의 주장은 "C와 C++는 특별한 특권적 지위를 가진다. 부트스트래핑 트리의 뿌리가 될 수 있도록 허용되는 유일한 자기 호스팅 언어들이다"라는 방향으로 퇴행하고 있습니다.
이 주장은 Reddit에 /r/rust/용 RSS 피드가 있던 10년 동안 여러 번 제기되었습니다. 사람들은 그때도 설득력 없다고 여겼고, 지금도 여전히 설득력이 없습니다.
GCC는 C++ 컴파일러를 필요로 하고, C++은 이제 너무 복잡해져서 Intel이 MSIE→Edge 같은 식으로 ICC를 LLVM 기반 물건으로 다시 만들기로 결정했으며, 그 결과 현실적인 사용 사례에 적합한 C++ 컴파일러는 사실상 세 개만 남았다는 점을 생각하면, GCC는 어떻게 부트스트랩합니까? (GCC, LLVM Clang, MSVC)
Rust도 이런 접근에서 혼자가 아닙니다. 내가 즉석에서 떠올릴 수 있는 예로 GHC (Haskell)가 있는데, 이것도 예전에는 Zig/OCaml식 부트스트래핑 접근을 사용했지만 이제는 compile-to-C 옵션을 종료했고, 새 플랫폼을 부트스트랩하려면 기존 플랫폼에서 크로스 컴파일할 것을 기대합니다.
여기서 예외치는 Zig와 Go입니다. (철학적으로도 그렇습니다... zig cc가 쉬운 크로스 컴파일을 위해 존재하고 이를 감싸는 cargo zigbuild가 존재하는 반면, "cgo is not Go"이며 Go는 적극적으로 고도로 최적화된 출력보다 빠른 컴파일러 런타임을 우선시하는 것을 수용하고, 더 쉬운 크로스 빌드를 위해 세상을 다시 발명하려는 성향이 너무 강해서 libSystem.dylib 우회를 철회해야만 했습니다. backstep 참고... 그런데 이것은 macOS 커널 ABI 안정성의 공식 경계입니다.)
바이너리 없이 Rust 부트스트래핑하기
첫째, 당신은 또다시 GCC에 특별한 특권적 지위를 부여하고 있습니다. (기억하시겠지만, 오늘날 GCC는 C++를 포함합니다.)
둘째, Rust에는 https://github.com/thepowersgang/mrustc가 있으므로, C++에서 1.74.0을 부트스트랩해 바이트 단위로 동일한 결과를 얻을 수 있습니다. (현재는 그렇습니다. 재부트스트랩 가능한 버전은 가끔 올라가기도 합니다.) Diverse Double Compilation을 통해 그렇게 한 뒤 현재 버전까지 차근차근 올라가면, 메인라인 바이너리가 Trusting Trust 공격에서 자유롭고 공개된 소스와 일치한다는 것을 증명할 수 있습니다.
(mrustc의 주된 목적이 바로 그것입니다. 기존 바이너리의 공개된 SHA256 해시가 향후 개발과 크로스 컴파일을 통한 새 플랫폼 부트스트래핑을 위한 신뢰할 수 있는 기반임을 증명하는 감사 도구입니다.)
개인적으로, 나는 압축된 699 MB tarball을 내려받고, 풀어서 1.9 GB로 만든 다음, 그 다음 Rust를 세 시간 반 동안 컴파일해서 겨우 다음을 사용할 수 있게 되는 것에 그다지 설득되지 않는다:
유감스럽지만, 대부분의 사람들에게는 당신이 패키지 트리 전체를 다시 부트스트랩하는 데 바치는 헌신이 XY Problem처럼 보입니다. 당신의 실제 문제는 비싼 중간 산출물(예: Rust 툴체인)의 캐싱을 철학적으로 거부하는 데 있는 것이지, 당신이 그것에 의존하는 것을 꺼리게 만드는 현재 캐싱 구현의 문제들을 해결하는 데 있는 것이 아닙니다.
솔직히 말하면, 내가 "Rust 부트스트래핑은 해롭다고 여겨진다"를 봤을 때, 내가 다뤄지리라 기대했던 관점은 바로 그것이었습니다.
내 조언은 이렇다: 지금 인기가 있다고 해서 무턱대고 모든 것에 Rust를 쓰지 마라. Rust가 정말 그 작업에 맞는 도구인지 신중히 생각하라. 좋은 대안은 많다:
Go는 "단순"하고, 세계 최고 수준의 크로스 컴파일과 정적 컴파일 바이너리를 기본 제공한다 (한때 내 하루를 구해준 적이 있다)
Go의 타입 시스템은 원시적이고, 제네릭을 받아들여야 한다는 점을 인정하기까지 너무 오래 걸렸으며, "모든 것은 가능한 한 단순해야 하지만 그보다 더 단순해서는 안 된다"라는 말에 대해 무엇이 가능한지 자체를 부정하고 엣지 케이스를 카펫 아래로 쓸어 넣는 사람들이 작성한 언어이고, 근본적으로 프로세스 내 FFI를 원하는 것은 잘못이라는 생각을 중심으로 설계되었습니다.
솔직히 말하면, 2010년대 중후반에 Rust의 ?를 보기도 했고 (아니, 사실 당시에는 try!()였던 것 같기도 합니다), 나는 Go를 한 번 보고 온갖 if err != nil에 질색했습니다.
OCaml은 훌륭한 시스템 프로그래밍 언어다. 대체로 UNIX 철학과 함수형 프로그래밍 패러다임을 따르며 설정도 쉽다. 어떤 사람들은 이것으로 타입 안전한 unikernel을 만든다
OCaml에는 가비지 컬렉터가 있습니다. GC는 고독한 생물입니다. Rust를 사용하면 한 번 코드를 작성한 다음 CLI 도구와 Python 같은 언어용 컴파일 확장 사이에서 그것을 공유할 수 있습니다. 이를 위한 도우미들도 있습니다.
한 번 작성해서 재사용할 수 있다는 것은 UNIX 철학의 원래 네 가지 핵심 요점 가운데 하나의 중심입니다.
정말 멋진 애들은 Zig를 쓴다
메모리 안전성 관점에서 보면, Zig는 머스터드에 살짝 찍은 C입니다.
대학에서 C/C++를 합쳐서 한두 학기 정도 들었던 것과, 각각 거의 기초와 Allegro 4를 이용한 게임 개발에 머물렀던 경험을 제외하면, 내가 C가 노력할 가치가 있다고 느낀 것은 MS-DOS 레트로 취미 프로그래밍을 시작하고 나서였습니다.
아주 작은 "셸 스크립트" 같은 것(덜 병리적인 원시 기능에 접근하기 위해 .py 확장자를 붙인 것)을 후다닥 쓰는 수준을 넘어설 만큼 규모가 있다면, 그것은 Zig보다 Rust가 훨씬 더 잘하는 것들로부터 이득을 볼 만큼 충분히 큰 것입니다. (ignore, Serde, Rayon, Clap, gumdrop 같은 CLI 도구에 유용한 패키지 생태계와, 무엇보다도 Rust Stability Promise를 포함해서 말입니다.)
작은 작업에는 C라는 공용어를 쓰면 되지 않나?
표준 라이브러리가 얼마나 황량한지, 그리고 그것을 보완할 의존성을 쉽게 추가하는 지원이 얼마나 없는지를 생각하면, 레트로 취미 프로그래밍에서조차, 프로젝트가 내가 보류 중인 BASIC 인터프리터와 실제 모드 x86 .zip SFX 스텁을 작성하는 작업이 아닐 경우에는 Free Pascal의 DPMI 타깃과 배터리 포함형 표준 라이브러리에 의존합니다. 그 보류 중인 프로젝트는 Inno Setup/NSIS 스타일 용도에 맞춘 표준 라이브러리를 제공하면서도 10KiB 이하에 맞춰 실제 내용물이 플로피 디스크에서 밀려나지 않도록 하는 것이 목표입니다. 당시 실제 설치 프로그램들보다 더 많이 차지하지 않게 하려는 거죠.
무엇보다도 다른 문제들이 있는데...
UB의 특별한 점은, 그것이 면역 체계를 공격하는 질병처럼 버그를 찾는 능력을 공격한다는 것이다. 정의되지 않은 동작은 임의적이고, 비국소적이며, 심지어 비인과적인 효과까지 가질 수 있어서 프로그램의 결정론적 성격을 무너뜨릴 수 있다. 그것은 용납할 수 없으며, 그래서 safe Rust가 여전히 제거하지 못하는 종류의 버그가 남아 있더라도 undefined behavior를 배제한다는 점이 그렇게 중요한 것이다.
-- trentj
Free Pascal과 Lazarus (그 Delphi 유사체)가 유지보수 관심 부족으로 Win9x 지원을 중단한 뒤로는 (구체적으로는 ANSI/비유니코드 Win32에 대한 지원), 내가 Linux로 옮기기 전 Visual Basic 6 다음이자 Python을 처음 배웠던 고대 버전의 Python 2.x + wxPython + py2exe로 할 수 없는 Windows 9x 레트로 취미 프로젝트로 넘어가게 될 때를 대비해 Rust9x를 눈여겨보고 있습니다.
(혹은, 내가 프로젝트가 Free but Shackled가 아니어야 한다는 기준을 지키는 것보다 일을 끝내는 것을 더 중시한다면, 수집 취미로 eBay에서 사 둔 Delphi나 Visual Basic 사본 중 하나를 그냥 사용할 수도 있겠죠.)
Hare는 단순하고, 안정적이며, 견고한 시스템 프로그래밍 언어다
와. 어디서부터 시작해야 할지...
Hare의 다양한 설계 결정에 대한 Drew Devault의 흥미로운 입장들(예시 하나는 여기)을 자세히 파고드는 대신, 이것만 인용하겠습니다:
그것은 사실상 우리가 주목해야 할 정당한 이유가 전혀 없다. 다른 곳에서 더 잘 해결되지 않는 실제 문제를 아무것도 다루지 않는다. Zig조차도 더 정당화되어 있지만, 그것도 아니다.
Hare는 창작자의 애정 속에서 C를 대체하도록 의도되었다. 그러나 C 코더들에게 채택되도록 의도된 어떤 언어도 실패할 운명이다. 남아 있는 C 코더들이란 이미 천 개의 언어가 스쳐 지나가는 것을 보았고, 그 모든 것을 넘겨버린 사람들로 정의되기 때문이다.
C++와 Rust 둘 다 생산성의 강력한 진보를 통해 C의 단점을 해결한다. 힘 빠진 C 업데이트는 잘해 봐야 주의를 분산시키는 것이고, 최악의 경우 대안들에서 코더들을 빼앗아 가며 문제를 더 악화시킨다. 그 언어들이 겨냥하는 사람들은 그것들을 원하지 않고, 새로운 프로그래머들은 실제로 더 나은 언어들을 배우는 편이 훨씬 낫다.
-- ncm @ https://lwn.net/Articles/893392/