3P by GN⁺ | ★ favorite | 댓글 1개
  • WebAssembly에 집중하기 위해 JavaScript에서 Rust로 옮긴 뒤 3년 동안 Wick, 프로덕션 배포, ebook, 약 100개 crates.io 패키지를 만들며 Rust의 실제 가치를 평가함
  • borrow checker, 풍부한 타입 시스템, 함수형 패턴, null 부재는 많은 오류를 컴파일 단계에서 막아 적은 테스트로 큰 코드베이스를 유지하게 해줌
  • Clippy와 Cargo workspace는 강력하지만, 전역 lint 설정과 workspace 배포처럼 도구·생태계의 빈틈이 운영 비용으로 이어짐
  • async, 리팩터링, generic·lifetime·trait constraint 관리는 JavaScript나 Go보다 마찰이 큰 영역으로 남아 있음
  • Rust는 견고하고 다재다능하지만 채용, 학습, 빠른 반복, 문제 추적 비용이 커서 범위가 명확하거나 초기 비용을 감당할 수 있을 때 더 적합함

WebAssembly가 Rust 선택을 이끌었음

  • 몇 년 전 WebAssembly에 100% 집중하기 위해 기존 작업을 내려놓았고, 당시 Rust가 WebAssembly 컴파일 지원이 가장 좋았음
  • 기능이 풍부한 WebAssembly 런타임들도 Rust 기반이어서 선택지 중 Rust가 가장 현실적인 옵션이었음
  • 이후 WebAssembly를 핵심 모듈 시스템으로 쓰는 애플리케이션 프레임워크이자 런타임인 Wick을 만들었음
  • 3년 동안 여러 프로덕션 배포, ebook, crates.io에 약 100개 패키지 배포를 거치며 Rust 경험이 쌓임

적은 테스트로 더 많은 코드 유지

  • Rust에서는 일반적인 언어처럼 테스트를 작성하다가, 컴파일만 되면 실패할 수 없는 테스트를 쓰고 있다는 점을 발견함
  • unsafe {} 블록과 .unwrap()처럼 panic이 나기 쉬운 메서드를 피하면 많은 문제가 기본적으로 회피됨
  • borrow checker, 풍부한 타입 시스템, 함수형 패턴과 라이브러리, null 값 부재가 테스트에 드는 노력을 줄임
  • Wick 프로젝트의 70,000줄 이상 코드를 다른 언어에서 필요했을 테스트보다 훨씬 적은 테스트로 유지했음
  • 테스트가 필요할 때는 Rust의 통합 테스트 하네스 덕분에 코드 옆에 쉽게 추가할 수 있음

Rust가 다른 언어에서의 코딩 습관도 바꿈

  • Rust 컴파일러는 다른 언어에서는 정상으로 여겼을 코드에도 계속 불만을 표시하고, 그 과정을 거치며 코딩 습관이 바뀜
  • 이제 다른 언어에서도 코드 줄 순서가 어색하거나 반환값을 확인하지 않으면 불편함을 느낌
  • 런타임 오류를 겪을 때도 이전보다 훨씬 강한 거부감을 느끼게 됨
  • Rust의 엄격함은 불편하지만, 컴파일러가 보호해주는 경험에 익숙해지면 다른 언어로 돌아가기 어려움

Clippy는 린터 이상으로 유용함

  • Clippy는 Rust의 린터지만, 단순 검사 도구보다 대체 코드를 제안하는 친절한 보조 도구에 가까움
  • Rust 표준 라이브러리는 매우 크고, 기능이 많은 타입·trait·매크로·함수에 흩어져 있어 필요한 API를 찾기 어려움
  • 여러 규칙은 표준 라이브러리 메서드나 타입으로 더 잘 대체할 수 있는 일반 패턴을 찾아냄
  • 수백 개 규칙이 성능, 가독성, 불필요한 간접화를 다루며 가능한 경우 대체 코드도 제공함
  • 프로젝트 전역 lint 설정은 Cargo 이슈를 통해 가능해질 것으로 보였지만, 그전까지 Wick은 수십 개 crate의 inline lint 설정을 스크립트로 자동 업데이트해야 했음

생태계에는 감수해야 할 빈틈이 있음

  • 전역 Clippy 설정 문제는 Rust 도구와 라이브러리에서 자주 마주치는 생태계 빈틈의 한 사례임
  • 관련 이슈들은 현재 닫혔지만, 여러 해 동안 열려 있었고 해결까지 오래 걸렸음
  • Rust는 오랫동안 “가장 사랑받는 언어”로 선정될 만큼 신규 사용자를 끌어들이지만, 그 흐름이 라이브러리와 도구의 극적인 개선으로 곧장 이어지지는 않았음
  • 특정 사용 사례를 처리하는 일회성 fork가 생기는 경우가 많았고, Wick에서도 PR을 넣으려 했지만 비슷한 상황을 겪었음
  • 가능한 이유로는 안정적인 API 유지 압박과 세분화된 타입 시스템이 있음
    • 라이브러리 소유자는 작은 변경도 주요 버전 변경으로 이어질 수 있어 받아들이기 어려움
    • 모든 사람의 요구를 만족하는 Rust 코드를 작성하는 부담도 큼

Cargo, crates.io, workspace 배포의 마찰

  • Wick 저장소 구조는 인기 프로젝트들을 참고해 만들었고 처음에는 합리적으로 보였지만, 배포 단계에서 문제가 드러남
  • Cargo로 모듈 크기의 crate를 빌드하고 테스트하고 사용하는 일은 쉽지만, crates.io 배포는 별개의 문제임
  • crates.io에는 참조하는 모든 crate가 개별적으로 배포되어 있어야 패키지를 publish할 수 있음
    • 로컬 파일시스템에만 존재하는 패키지에 의존한 crate를 배포하지 못하게 하는 점은 타당함
    • 하지만 큰 프로젝트를 작은 내부 모듈로 나누는 자연스러운 구조에서는 부모 crate 안에만 존재하는 sub-crate를 포함해 publish할 수 없음
  • 로컬 dev dependency가 있는 crate도 Cargo.tomlversion을 포함하지 않으면 publish할 수 있다는 정정이 있었음
  • Cargo workspace 지원 자체는 훌륭하고, 대형 프로젝트 관리 경험은 대부분의 언어보다 좋음
  • 그러나 workspace는 배포 문제를 해결하지 못하며, 설정 방식이 여러 가지여도 쉽게 배포되는 “정답”은 찾기 어려움
  • cargo workspace publish 관련 유틸리티 crate가 많이 존재하는 점 자체가 문제를 보여줌
  • Wick을 publish할 때는 수동 반복 작업과 부분적으로만 동작하는 도구를 조합하느라 1시간 이상 걸리는 경우가 자주 있음

async는 가장 큰 마찰 중 하나

  • Rust의 async는 언어가 처음 만들어진 뒤 추가된 기능처럼 느껴지고, 실제 사용에서도 뒤늦게 붙은 기능처럼 자주 방해가 됨
  • 오류를 이해하고 해결하기 어렵고, 해법을 찾을 때도 여러 런타임과 각자의 async 방식을 기준으로 걸러야 함
  • 어떤 async 라이브러리는 특정 async 런타임 밖에서는 사용할 수 없을 가능성이 있음
  • JavaScript를 20년 사용하고 Go 경험도 있는 입장에서 Rust async는 가장 큰 좌절과 마찰의 원천임
  • 극복 불가능한 문제는 아니지만, async 문제가 언제든 나타날 수 있음을 항상 대비해야 함
  • 다른 언어에서는 async가 거의 보이지 않을 정도로 자연스럽게 작동함

리팩터링은 고된 작업이 될 수 있음

  • Rust의 풍부한 타입 시스템은 장점이자 단점임
  • Rust 타입으로 사고하는 일은 좋지만, Rust 타입을 관리하는 일은 악몽이 될 수 있음
  • 데이터와 함수 시그니처에는 generic type, generic lifetime, trait constraint가 들어갈 수 있음
  • constraint 자체도 다시 generic type과 lifetime을 가질 수 있어, 실제 코드보다 타입 constraint가 더 많은 경우도 있음
  • 모든 impl마다 generic을 정의해야 해서 처음 작성할 때도 번거롭고, 리팩터링 때는 작은 변경이 연쇄 수정으로 커질 수 있음
  • 같은 constraint나 generic 목록을 여러 곳에서 반복해야 할 때 이를 alias하거나 중앙 정의로 참조할 언어·도구 차원의 방법이 없어 중복 부담이 남음

최종 판단: 강력하지만 비용이 큼

  • Rust는 시스템 수준 코드, CLI 앱, 웹 서버, 웹 클라이언트를 같은 언어로 작성할 수 있을 만큼 다재다능함
  • WebAssembly를 사용하면 같은 바이너리로 LLM을 브라우저와 명령줄에서 실행할 수 있음
  • Rust 프로그램은 매우 견고할 수 있고, Rust가 막아주는 문제를 체감하면 다른 언어로 돌아가기 어려움
  • Go로 잠시 돌아갔을 때 개발 속도는 다시 매력적이었지만, 런타임 panic을 겪은 뒤 장점이 흔들렸음
  • Rust에는 분명한 단점이 있음
    • 채용이 어려움
    • 학습이 느림
    • 빠른 반복에는 너무 경직됨
    • 특히 async 코드에서 메모리와 성능 문제를 추적하기 어려움
    • 모든 라이브러리가 안전한 코드에 충분히 좋지는 않음
    • 개발 도구는 더 개선될 여지가 큼
  • 작은 팀으로 놀라운 일을 했지만 큰 장애물도 있었고, Rust가 더 적합했던 기술적 이유도 있었기 때문에 Wick에 Rust가 가치 있었는지는 아직 이르다고 판단함
  • 빠르게 반복해야 한다면 Rust는 적합하지 않을 가능성이 높음
  • 범위가 알려져 있거나 더 큰 초기 비용을 감당할 수 있다면 Rust를 진지하게 검토할 만함
  • WebAssembly 관점이 매달 강해지면서 한 번 작성한 견고한 소프트웨어를 여러 곳에서 재사용하는 가능성이 더 현실에 가까워지고 있음

댓글과 토론

Hacker News 의견들
  • Rust를 많이 썼지만 몇 년이 지나도 여전히 생산성이 낮게 느껴짐
    요즘은 Zig를 많이 쓰는데, 원하는 코딩에만 집중할 수 있고 어떤 도구나 라이브러리를 써야 할지 고민하지 않아도 돼서 10배쯤 더 생산적인 느낌임
    Rust가 메모리 안전성을 주고 그게 중요하다는 건 알지만, 사용성이 정말 나쁨. Rust를 쓸 때마다 제약받는 느낌이고, 늘 라이브러리를 찾거나 작업 방법을 검색해야 해서 그냥 “코드를 타이핑”할 수가 없음
    타입 시스템도 통제 불능처럼 커질 수 있고, 어떤 구조체에서 실제로 어떤 메서드를 호출할 수 있는지 알기 어려울 때가 많음. Rust는 훌륭한 도구이고 많은 문제를 해결하지만, 좋은 범용 언어라고는 생각하지 않음

    • 그 느낌이 모두에게 공통인 건 아님. 셸 스크립트보다 조금이라도 복잡한 작업이면 Rust를 쓰고, 내 창 관리자도 Rust 프로그램으로 제어함
      Python을 거의 20년 써온 입장에서 말해도, 지금은 Rust에서도 Python만큼 빠르게 작업함
    • 동의함. 지금 시점에서는 Rust보다 C와 C++ 에서 훨씬 더 생산적이라고 느낌
      Rust는 내 기준으로 적절한 균형점을 완전히 놓친 것 같음. 고수준 애플리케이션을 쓰기엔 저수준 세부사항에 너무 까다롭고, 임베디드나 운영체제를 쓰기엔 너무 복잡함
      전자라면 C++, Java, Haskell, OCaml, 심지어 Go에 C를 조금 섞는 쪽을 택하겠고, 후자라면 매크로 어셈블리처럼 쓰는 C가 훨씬 적합함
      Graydon Hoare의 원래 구상, 즉 선형 타입, 가비지 컬렉션, 스택 할당, 그린 스레드, CPS가 있는 OCaml/SML 쪽이 훨씬 나은 언어가 됐을 것 같다는 느낌이 아직 있음
    • 내 경험은 정반대임. 임베디드 시스템에서 Rust를 쓰면서 자신감과 속도가 크게 좋아졌음
      C에서는 작은 실수 하나가 정의되지 않은 동작과 골칫거리로 이어지는 일이 잦지만, Rust에는 그런 게 없어서 완전히 판도를 바꿔줬음
    • 어떤 종류의 코드를 쓰는지 궁금함. 아주 저수준인지, 아니면 아주 고수준인지?
      Zig에서는 라이브러리를 찾거나 어떻게 해야 하는지 알아낼 필요가 없다는 뜻인지 궁금함
    • 전반적인 느낌에는 동의하는데, 말로 풀어내기가 어려움
      Rust는 각 비트와 바이트가 어디로 가는지, 어느 스레드에서 쓰이는지, 어떤 변경 방식으로 다뤄지는지를 미리 결정하라고 강요함. 파서나 마이크로컨트롤러 수준이 아니면 이 과정이 지루하게 느껴짐
      뭔가를 먼저 동작하게 만든 뒤 최선의 API 구조를 정하는 방식을 좋아하는데, Rust는 그 과정과 충돌함
      Rust의 타입 시스템이 더 강력하긴 해도, Swift로도 성능의 90%는 얻을 수 있고 훨씬 자연스럽게 흘러감
  • crates.io에 이름공간이 없다는 점이 가장 큰 비판점일 수 있음
    누구나 전역의 일반적인 패키지 이름을 선점할 수 있고, crates.io 저장소를 피하지 않는 이상 대부분은 그걸 감수해야 함. 그런데 그렇게 선점된 일반 이름의 패키지 중 일부는 실제로 쓰기에 가장 좋은 패키지가 아님
    Java식 역순 DNS 표기가 장황하고 성가셔서 그에 대한 반작용이었을 수도 있지만, GitHub처럼 사용자/그룹 이름공간을 패키지명 앞에 붙이는 방식이 좋은 중간점이었을 것 같음

    • crates.io에서 이름 선점자를 찾는 분석을 해봤고, 최상위 선점자가 일주일 내내 약 30초에 하나꼴로 크레이트를 만든다는 계산이 나왔음
      그 분석을 crates.io 팀에 보냈고 자동화 금지 정책이 있다는 점도 짚었지만, 이름을 선점했다는 충분한 증거가 아니라고 답했음
      crates.io의 문제는 명확한 정책이 있어도 집행하지 않는다는 것임. 그래서 짧고 기억하기 쉬운 크레이트 이름은 이미 다 가져가 있고, 되찾을 방법도 없음
    • Java식 역순 DNS에 대한 반작용이라기보다는, 당시 대부분의 패키지 관리자가 세운 관례를 따른 것에 가까워 보임
      NPM, PyPI, RubyGems, Elixir의 Hex, Haskell의 Cabal 등 Rust가 나왔던 2014~2015년 무렵 Java가 아닌 패키지 관리자 중 단일 전역 이름공간이 아니었던 사례가 잘 떠오르지 않음
      일부는 이후 이를 고치려 했지만, 당시에는 그냥 패키지 관리자가 그렇게 동작하던 시절이었음
    • Maven과 Java는 의존성 관리가 잘 동작한다는 점에서 충분한 평가를 받지 못함
      이후 나온 다른 언어들의 더 못한 의존성 관리 시스템은 앞선 사례에서 거의 배우지 못했음
    • 패키지에 URL을 쓰는 방식은 꽤 말이 됨. Go 생태계에서 잘 작동함
      언어 차원에서 전역 패키지 데이터베이스가 필요 없어지는 장점도 있음. example.com/your-thing에 패키지를 올리면 곧바로 릴리스되는 셈임
      물론 원한다면 캐시와 검색 엔진은 따로 제공할 수 있음
    • Rust를 자주 쓰지는 않지만, 이런 문제는 패키지 저장소 전반에서 정말 성가심
      http-server는 나쁘니 쓰지 말고 MuffinTop을 써야 한다는 식인데, 그걸 그냥 알고 있어야 함
      공인된 패키지 이름이라는 개념은 흥미롭지만, 시간이 지나며 별칭 뒤의 코드가 바뀌면 실제로는 혼란스러울 가능성이 큼
      결국 어떤 생태계에서든 도메인 전문가가 되는 과정의 일부로 계속 남을 것 같음
  • 워크스페이스 루트에 .cargo/config.toml을 만들면 모든 크레이트에 적용되므로 전역 Clippy 린트를 설정할 수 있음
    파일 안에는 [build] 아래에 rustflags = ["-Wclippy::lint_name_to_warn", "-Dclippy::lint_name_to_deny"]처럼 넣으면 됨
    단, rustflags는 추가 방식이 아니라 덮어쓰기 방식이라 RUSTFLAGS 환경 변수 같은 다른 출처가 있으면 이 설정을 덮어씀

    • 우리는 커밋 시 lib.rsmain.rs 파일에 린트를 추가하는 스크립트를 돌림. 간단함
  • Rust가 직업적으로 중요해질 게 분명해 보여서 배우고 있음. 정말 좋아하고 싶고 장점도 보이지만, 지금까지 써본 언어 중 가장 불쾌한 축에 들어감
    숙련도가 오르면 싫어하는 마음이 사라지길 계속 기대했지만, 학습 곡선을 오를수록 딱히 정이 붙지는 않음
    괜찮음. 능숙하게 다루면서도 싫어하는 언어가 이것만은 아닐 것임. 다만 Rust를 사랑한다고 말하는 사람이 너무 많아서 나도 즐길 줄 알았음

    • 반례로, 나는 Rust 프로그래밍을 좋아함. 빌림 검사기와 싸우던 시기는 오래전에 끝났고, 요즘은 타입을 확인하려고 일부러 유발하는 경우를 빼면 오류도 드묾
      Rust 컴파일러도 유효한 더 넓은 경우를 받아들이도록 좋아진 것 같음
      빌림 검사기를 이해하는 핵심은 바탕이 되는 메모리 모델을 이해하는 것이었음. Rust의 메모리 모델은 제네릭 같은 추상화를 위한 확장을 제외하면 C와 같음
      빌림 검사기 규칙은 처음엔 임의적으로 보이지만 이 메모리 모델과 깊이 연결돼 있음. 의도치 않게 빌림 검사기에 걸릴 때가 진짜 가치인데, 그건 주의가 흐트러져 만든 버그이기 때문임
      무서운 건 C나 C++ 같은 언어라면 그런 코드를 그냥 받아들이고 진행할 수 있다는 점임
      Rust의 엄격한 타입 시스템과 빌림 검사기는 코드를 제대로 구조화하도록 부드럽게 밀어주며, 내가 쓰는 모든 언어에서 코드 설계를 개선해줬다고 확신함
    • Rust를 익히려고 여러 번 시도했지만 계속 튕겨 나옴. 그냥 사용성이 좋지 않은 느낌임
    • 아직도 걸리는 건 기본값/이름 있는 함수 인자가 없다는 점임
      인기 있는 대부분의 언어가 공유하는 아주 기본적인 프로그래머 사용성 기능이고, C++조차 오래전부터 기본 인자를 갖고 있음
    • 무엇이 싫은지 궁금함
  • 10년 동안 거의 매주 pthread를 호출하던 전직 C/C++ 프로그래머인데, 지금은 비동기 Rust를 어디서나 씀
    비동기가 왜 그렇게 미움을 받는지 모르겠음. 내 생각에는 모두가 모든 것에 비동기를 써야 함. 겉보기엔 단일 스레드인 “간단한” 작업까지 포함해서

    • 문제는 전염성이라고 봄. 특히 임베디드나 Wasm 환경에서는 주류 비동기가 내가 원하는 비동기가 아닐 수 있음
      작성자의 사용 사례가 Wasm이었다면 분명 다른 관점이 생겼을 것임
      큰 버퍼를 쓰거나 재사용해 할당 비용을 피하는 작업은 예전식 스레드 풀의 이점을 받는 경우도 많음. 범프 할당자나 샤드 할당자로 어느 정도 해결할 수 있지만, 벡터화 가능한 빡빡한 루프에서 CPU에 묶이는 경우에는 스레드 풀이 더 잘 동작함
      비동기는 좋은 도구지만, 최적인 맥락만 있는 건 아님
    • 중첩된 비동기 호출에서 오류 처리가 Go와 어떻게 다른지 비교하려고 최근 비동기 Rust를 조금 써봤는데, 작성하려던 사소한 예제조차 Tokio 없이는 안 되는 것처럼 보였음
      Go, C#, TypeScript에는 그런 장벽이 없음
      예를 들어 Tokio에서 가져오는 데코레이터 없이는 main 함수 안에서 await도 못 하는 것 같음
    • 지금 부딪힌 문제는 rhai Engine 같은 몇몇 항목이 Send가 아닌데, 이를 비동기 클로저 안에서 쓰려 한다는 점임
      GPT-4는 스레드 안에 tokio Runtime을 만들고 block_on()을 쓰라고 제안했음. 내일 시도해볼 예정임. 이게 내 첫 본격 Rust 프로젝트임
    • 나는 반대로 배웠음. 프로그래머들이 필요하지도 않은데 모든 걸 불필요하게 비동기로 만들어서 복잡성과 정신적 부담을 늘리는 경향이 있음
    • 좀 더 자세히 설명해줄 수 있나? 겉보기엔 단일 스레드인 간단한 작업 중 비동기를 선택할 만한 예가 무엇인지 궁금함
      “이제 비동기 Rust를 어디서나 쓴다”는 말이 “Tokio를 어디서나 쓴다”는 뜻인지도 궁금함
  • Rust 프로그래밍은 학대적인 관계와는 정말 다름. 컴파일러는 최대한 도와주려 하고, 특히 rustc 오류 메시지는 세계 최고 수준임

    • 운영체제는 프로그래머가 자원을 올바르게 다루길 바라며, Rust 컴파일러는 그 일을 아주 쉽게 만들어줌
      학대적인 관계에 더 가까운 건 Rust 컴파일러가 아니라 운영체제 쪽임. 하드웨어도 마찬가지로 어셈블리를 올바르게 실행해야 하니 그런 식으로 보면 학대적인 관계라고 할 수 있음
      Rust의 오류 메시지는 독보적이고, 가까이 오는 컴파일러도 없음
      덧붙여 최근 LaTeX를 썼는데 오류 메시지가 끔찍했음. 무엇이 잘못됐는지 오류를 들여다보며 파악하는 과정이 악몽 같았음
    • 대체로 좋지만, 비동기 함수 안의 어떤 오류든 모든 재귀 호출 지점에서 Future가 더 이상 SendSync가 아니라는 오류를 만들어내는 건 정말 싫음
      콘솔 전체가 오류로 뒤덮이고, 실제 문법 오류는 그 중간 어딘가에 묻혀 있음
    • Rust 컴파일러는 내가 본 것 중 처음으로 “perhaps”라는 단어를 쓰는 컴파일러였음
      주된 불만은 아직도 수명(lifetime)을 완전히 이해하지 못한다는 점이고, 컴파일러도 매번 도와줄 수는 없음. 이해는 되지만 컴파일러가 보수적으로 판단하기 때문임
  • C++ 테스트에서 흔히 “컴파일되면 대체로 맞다”는 말을 함
    Rust가 많은 오류를 처리해줘서 흔한 테스트 사례가 무의미해진다고 생각한다면, 다른 언어에서 올바른 것을 테스트하지 않았다는 신호라고 봄
    테스트해야 할 것은 언어 자체의 문제점이 아니라 비즈니스 로직
    테스트 코드를 보고 “JavaScript라면 테스트하겠지만 Rust에서는 안 해도 된다”고 느껴진다면 그 테스트는 그냥 지우면 됨

    • “컴파일되면 대체로 맞다”는 말은 Haskell과 Rust에서는 들어봤지만, C++ 에 적용하는 건 들어본 적이 없음
    • 널 포인터 예외는 비즈니스 로직을 깨뜨리는 버그임
      “비즈니스 로직 대 언어 문제”라는 구분은 없음. 언어가 비즈니스 로직이 놓이는 토대이기 때문임
      실패 방식에 대해 테스트하지 않는다면 테스트의 의미가 무엇인지 모르겠음
    • 깨진 쓰레기 코드가 컴파일됐으니 아마 맞을 거라고 여기는 C++ 프로그래머 유형에는 묘한 대단함이 있음
      익숙한 대부분의 언어와 달리 C++에는 IFNDR가 있고, 농담처럼 “이것이 C++ 프로그램인가?”라는 질문에 대한 거짓 양성이라고 불림
      표준을 따르는 C++ 컴파일러는 작성한 코드가 말이 안 된다고 의심되는 일부 경우에 알려주는 것이 금지되고, 그냥 계속 진행해 뭔가를 출력해야 함
      그게 동작하는 실행 파일일 수도 있고, 금요일마다 대참사를 일으키는 실행 파일일 수도 있음. 알 방법이 없음
      ISO 표준이 이런 경우를 식별하긴 하지만 너무 모호해서 무엇이 포함되는지 정확히 잡기 어렵고, 내 추측으로는 요즘 대부분의 비 trivial C++ 소프트웨어가 실제로 IFNDR일 것임. 그냥 언어 전체를 거부하는 편이 나음
  • Rust는 프로그래머가 컴파일러가 하는 모든 일을 완전히 통제하고 의식해야 한다는 생각을 마침내 깨뜨린 것 같음
    사실 수십 년 전부터 그렇지 않았고, 컴파일러는 거의 마법에 가까웠음. Rust는 빌림으로 그 흐름을 많이 되돌려, 사람들이 컴파일러가 자신보다 더 잘 안다는 점에 편해지게 만들었음
    더 나아가 편해졌으면 좋겠음. 알고리즘상 필요한 경우가 아니라면 컬렉션을 명시적으로 앞에서부터 반복할 필요가 없어야 함. 여러 작업은 암묵적으로 병렬화되어야 함. Rust다운 Bash가 있으면 좋겠음

    • 내 Rust 경험과는 완전히 반대임
      Rust는 자신이 하는 일이 꽤 투명하고, 컴파일러 마법에 매우 보수적임. 언어가 힙 할당을 하지 않고, 참조 카운팅도 하지 않으며, 암묵적 숫자 타입 변환도 없음
      암묵적으로 복사 가능하다고 선언하지 않은 타입은 복사하지 않고, 그마저도 단순한 얕은 memcpy로 복사 가능한 타입에서만 합법임
      Rust는 곳곳에서 무비용 추상화를 쓰므로 어떤 코드로 컴파일될지 예측 가능하고, 보통은 단순함. 표준 타입의 기본 배치도 잘 알려져 있어 Vec 순회가 포인터를 증가시키는 루프로 컴파일될 것을 알 수 있고, 암묵적 병렬성은 없음
      빌림을 “컴파일러가 프로그래머보다 더 잘 아는 것”으로 묘사하는 건 이상함. 빌림은 타입 검사와 비슷함. 어떤 타입을 임시라고 선언해놓고 오래 사는 것처럼 쓰려 하면 오류가 남
      Foo 구조체를 반환한다고 선언한 함수에서 Bar를 반환하면 오류가 나는 것과 같음. 컴파일러가 “더 잘 아는” 이유는 그냥 버그를 썼기 때문임
      빌림도 가비지 컬렉션 없이 직접 포인터 사용으로 컴파일되며, C ABI 구조체와 함수에서는 말 그대로 C 포인터와 동일하다고 보장됨. 컴파일러보다 더 잘 안다고 생각하면 unsafe로 수명을 우회할 수도 있음
    • 병렬 반복자를 제공하는 크레이트가 이미 있음. iter() 호출 이름만 바꾸면 됨
      다만 그게 암묵적이어야 한다는 데는 동의하지 않음
    • “컴파일러가 하는 일을 통제하지 못한다”는 표현은 조금 과장임
      어떤 면에서는 Rust가 C보다 프로그래머에게 더 많은 통제를 줌. 예를 들어 Rust는 인라인 어셈블리를 표준으로 지원하지만, C의 인라인 어셈블리는 공급자별 확장에 의존함
      다만 편한 기본값은 매우 다름. Rust에서 안전하지 않은 타입 캐스팅은 많은 절차와 신중함을 요구하고, C보다 더 많은 규칙을 따라야 함
      특히 Rust의 참조는 사실상 전부 restrict처럼 동작하며, 원시 포인터에서 안전한 참조로 unsafe 캐스팅할 때 이를 망치기 아주 쉬움. 그래서 선택지가 있다면 그런 코드를 쓰지 않게 되는 강한 유인이 생김
    • 태그 포인터 같은 것을 포함해 unsafe Rust를 많이 쓰는 입장에서 강하게 동의하지 않음
      오히려 더 명시적인 통제를 원하고, 더 표현력 있는 타입 시스템으로 그 부담을 줄이고 싶음. 이상적으로는 Rust의 타입 시스템이 Prolog 변종처럼 되면 좋겠음
    • “Rust다운 Bash”라면 타입, 특히 부동소수점이 있고, 예외 상황이 적고, 명시적 매개변수를 가진 함수와 단순한 명령줄 플래그가 있는 Bash일 것임
  • 언제 타입 제약에 투자하고 언제 하지 않을지를 배우는 건 중요한 교훈임
    Rust만의 문제는 아니지만 표현 방식이 조금 다를 수 있음
    과도하게 타입화된 C++와 지나치게 추상화되고 타입화된 Java를 다뤄봤고, 둘 다 같은 종류의 리팩터링 문제를 가짐
    반대로 타입이 부족하고 문서화도 부족한 Go도 많이 봤는데, 특정 값들이 여기저기 흩어져 런타임 지뢰가 되며 리팩터링이 정말 심각하게 어려워질 수 있음
    초기 진행감은 더 빨리 오지만 대개 버그를 사용자에게 배포하게 됨
    이 절충에는 마법 같은 정답이 없음. Rust는 이 축에서 선택지를 꽤 넓게 제공하는 편임

  • “Rust가 하루 종일, 매일, 예전 삶에서는 완전히 정상으로 여겼을 것들에 대해 소리친다”는 말은 좋은 C 컴파일러도 모든 플래그를 켜면 비슷하게 함
    선택적으로 소리침을 끄고 일부러 나쁜 코드를 쓸 수 있게 해주는 언어와 컴파일러를 좋아함. 빠르게 작성할 수 있는 동작하는 나쁜 코드는 완벽하지만 영원히 걸리는 코드보다 나을 때가 많음
    나쁘지만 동작하는 개념 증명을 만든 뒤 덜 나쁘게 고치면 됨
    “새 사용자는 잘 끌어오지만 라이브러리나 도구가 극적으로 좋아지지는 않고, 특정 사용 사례를 처리하는 일회성 포크만 생긴다”는 점은 나이와 무관함
    핵심 개발자를 끌어들이는 건 어렵고, 매력적으로 만들기 위해 많은 노력이 필요함. 게다가 문화적 관례는 초기 도입자가 정하며, 관례가 없는 것도 나쁜 관례만큼 해로울 때가 많음
    Python을 보면 개발과 실행 환경에 느슨하게 접근한 결과, Python 프로그램을 개발하거나 실행하는 방식이 50개쯤 경쟁하게 됐음
    가장 많이 쓰이는 패키지 저장소 PyPI는 수년간 엉망이었고, 기존 패키지 위에 쌓아 올리는 사람은 적고, 이름은 무작위 단어 생성기처럼 보이며, 생태계에는 악성 코드가 많고, 명령줄에서 패키지 검색조차 못 함
    이건 언어의 잘못이 아니라, 방관자처럼 있던 커뮤니티와 핵심 팀의 잘못임. 문화는 그 중심의 기술보다 더 중요함
    Python만 집어 비판하려는 건 아니고, 그 문제를 더 잘 알기 때문임. C는 반세기 동안 있었지만 그 커뮤니티도 더 현대적인 언어들이 마련한 해법의 절반도 제대로 정리하지 못했음

    • Rust도 그걸 지원함. 전부 unsafe로 표시하면 됨
    • “개선된 라이브러리가 없다”는 이유로 Linux와 Windows가 컴포넌트를 Rust로 포팅하기 시작했을 리는 없을 것임
    • 과감히 말하자면, 그건 TypeScript와 많이 닮았음
      컴파일러가 고치라고 소리치지만, 완벽하게 다듬기 전에 그냥 아이디어를 시험해보고 싶은 상황 말임