이번 포스팅에서는 GoLang, Zig, Rust를 비교해 보겠습니다. 그리고 Rust가 이 비교에서 승리한 이유는 무엇입니까?
저는 GoLang, Zig & Rust로 3개의 프로젝트를 작성했습니다. 이러한 프로젝트는 언어의 기본, 단점, 언어, 프레임워크 또는 생태계에 문제가 있는지에 대해 좋은 아이디어를 얻을 수 있을 만큼 충분히 큽니다.
팁: TLDR 섹션으로 이동하여 답을 얻으세요.
몇 달 전부터 처음에는 GoLang을 사용하여 개발자 도구를 구축하기 시작했습니다.
첫 번째는 기본 데이터베이스 관리 및 마이그레이션 유틸리티(dbdaddy)였으며 GoLang을 사용하여 정말 재미있게 작업했습니다.
Advent Of Code 및 10억 행 챌린지 문제보다 더 심각한 것을 GoLang으로 구축한 첫 시도였지만, 숙달되는 데 일주일도 채 걸리지 않아 놀랐습니다 .
그동안 저는 옆에서 Zig와 장난을 치고 있었습니다(심각한 것은 아닙니다). Redis가 라이선스를 "기술적으로 비오픈 소스" 라이선스로 변경했다는 소식을 들었습니다.
그리고 저는 속으로 생각했습니다. zig에서 새로운 오픈 소스 Redis를 구축하는 것이 얼마나 어려울까요?
2개월 후 빨리감기: 젠장.
'GbCache'의 기본 아이디어는 LRU 기반 인메모리 캐시였지만 실제 정보 소스는 특정 키/값 변경에 대한 알림 및 분명히 키에 할당된 TTL과 같은 기능과 함께 디스크에 저장되는 것이었습니다.
Zig가 얼마나 명확하고 작업하기가 더 간단하고 쉬운지 인상적입니다.
'내일 HyperScale™으로 간다면 부하를 감당할 수 있을까?'라는 생각으로 GbCache를 시작했습니다.
이러한 관점으로 인해 저는 GoLang을 배제하게 되었습니다. 왜냐하면 메모리 할당에 대해 극도로 확실하게 추론할 수 없다면 속도를 완전히 제어할 수 없기 때문입니다.
이러한 관점에서 작업을 수행하는 것은 정말 훌륭한 학습 경험입니다. 갑자기 프로그램에서 폭발하거나 메모리가 부족하거나 병목 현상이 발생할 수 있는 모든 지점이 보이기 시작하기 때문입니다. 코드의 핫 경로.
Zig의 명시성 덕분에 메모리를 언제 어디에 할당하는지 정확히 알 수 있었습니다.
하지만... Zig는 결국 메모리에 안전한 언어가 아니며 저는 에베레스트 수준의 기술 문제를 가진 사람입니다.
녹병 발생
지난 2년 동안 저는 Rust를 3번 시도하고 그만 두었습니다. Macbook m69 Max에 엄청난 GB RAM이 있는 JavaScript 배경에서 저는 Rust가 왜 그런 규칙을 가지고 있는지 결코 정말 이해하지 못했고, 과대 광고에 참여하는 것은 아마도 관점에서 보기에 나쁜 각도였을 것입니다. .
몇 번이나 화를 냈음에도 불구하고 Rust는 여전히 내 마음 한구석에 있었고, 이상한 구문, 실망스러운 메모리 규칙, 놀라운 수명이 있었습니다.
그런데 Zig를 쓰던 중 뭔가 충격을 받았습니다... 이미 모든 기억 규칙과 수명을 추적하고 있었지만 정신적 오버헤드로.
나는 Rust가 이미 하고 있는 일을 시도하면서 내 Zig 코드에서 패턴과 상용구를 발견하기 시작했습니다.
SFS(SFW 이름 - "Simple File Storage")용 CLI 클라이언트를 구축하는 동안 저는 Rust에 다시 한번 시도해 보기로 결정했습니다.
이번에 러스트북을 넘기면서 이 모든 것의 '왜'를 느끼기 시작했습니다.
제가 Rust에서 좋아하는 가장 근본적인 점은 메모리의 소유권 및 차용 기반 멘탈 모델입니다.
이 소유 및 참조 메모리 모델은 상상적이지만 추론하기 편리한 정신 모델이고 머리에 드는 오버헤드도 적습니다.
C 및 Zig와 같은 언어가 머리에 발생하는 고전적인 정신적 기억 모델과는 반대입니다. 개별 메모리 할당과 수명을 머릿속으로 추적해야 합니까? 아니요, 감사합니다!
당신이 일상적인 백엔드 엔지니어이고 스프린트 성능을 계산하는 것을 좋아한다면 아마도 GoLang이 최선의 선택일 것입니다.
"하지만 우리 회사는 GoLa를 사용하지 않습니다."
"퇴사하라, 퇴사하라, 공구에 맞는 일을 하라. 퇴사하라."
나에게 GoLang은 서두르는 느낌 결코 마음속으로 느껴지지 않지만 결코 실망시키지 않는 중매결혼과 같습니다. GoLang은 당신의 목숨을 걸고 평생을 걸 수 있는 동반자입니다. 입니다.
하지만 내 친구가 급하게 찾고 있다면 계속 읽어보세요!
부— 하지만 난 서두르고 싶어... 그들의 ey-구문을 보는 것만으로도 가슴이 터지는 걸 느끼고 싶어... 그들과 함께 있으면 천국에 있는 것 같은 기분을 느끼고 싶어 내가 없을 때...
Rust의 멀티스레드 안전성에 대한 예를 간단히 살펴보겠습니다.
use std::thread; fn main() { let mut counter = Box::new(0); // 32-bit integer allocated on the heap let mut handles = vec![]; for _ in 0..10 { let handle = thread::spawn(|| { *counter += 1; // trying to edit the value }); handles.push(handle); } for handle in handles { handle.join().unwrap(); // WAITING FOR ALL THE THREADS TO COMPLETE } println!("Result: {}", *counter); }
힙에 할당된 값이 있고 10개의 스레드가 이를 독립적으로 수정하려고 합니다.
이 코드는 모든 스레드가 동시에 카운터를 수정하려고 시도하므로 카운터 변수에 안전하지 않게 액세스합니다.
다른 언어에서는 유사한 코드가 쉽게 컴파일되고 실행됩니다.
이전에 멀티스레드 환경에서 작업한 적이 있다면 카운터 변수 업데이트 시 경쟁 조건을 방지하기 위해 "뮤텍스"를 사용하는 것이 가장 먼저 생각할 것입니다.
하지만 큰 코드베이스에서는 사람의 실수로 인해 이와 같은 작은 것들을 놓치기 쉽습니다.
Rust는 이 프로그램의 컴파일도 허용하지 않습니다.
소유권 및 차용 규칙으로 인해 컴파일러는 for 루프의 첫 번째 반복의 첫 번째 스레드에서 가변적으로 차용 카운터를 감지합니다... 지금까지는 괜찮습니다... 두 번째 반복 또한 카운터를 병렬로 가변적으로 빌리고 싶나요? 안전하지 않아요! COMPILE-TIME ERROR를 발생시킵니다!!.
변경/수정하려는 의도로 소유자의 값을 참조하거나 "대여"하는 것을 "변경 가능한 차용"이라고 합니다
use std::thread; fn main() { let mut counter = Box::new(0); // 32-bit integer allocated on the heap let mut handles = vec![]; for _ in 0..10 { let handle = thread::spawn(|| { *counter += 1; // trying to edit the value }); handles.push(handle); } for handle in handles { handle.join().unwrap(); // WAITING FOR ALL THE THREADS TO COMPLETE } println!("Result: {}", *counter); }
이런 상황에서 다른 언어에서는 전혀 오류가 발생하지 않습니다. 정확성을 검사하는 것은 귀하의 책임이며 Rust는 이를 방지합니다.
언어 전반에 걸쳐 퍼져 있는 이와 같은 구문은 (자주) 발에 총을 쏘지 않도록 도와줍니다.
상황에 따라 다릅니다!
저는 정말로답할 수 있는 언어가 있었으면 좋겠습니다. GoLang이 그에 근접하더라도 사용 사례에 따라 다르며 가장 중요하게는 팀에 따라 다릅니다.
저에게 Rust는 함께 일하는 것이 즐겁습니다(지금은 누가 알겠어요... ㅎㅎ)
TigerBeetle 직원들은 Zig를 사용해 보았고 매우 만족했습니다.
프리미아젠은 지그와 함께 더 행복해졌습니다.
Jarred Sumner의 경우 Zig가 괜찮습니다. 그는 Zig에서 Bun을 썼습니다.
Mitchell Hashimoto(HashiCorp 뒤에 있는 사람)는 엄청나게 빠른 터미널인 Zig에서 유령 같은 글을 쓰고 있습니다.
.
.
.
잠깐...
위 내용은 나는 모든 인기 프로그래밍 언어를 시도했습니다의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!