PGR21.com
- 자유 주제로 사용할 수 있는 게시판입니다.
- 토론 게시판의 용도를 겸합니다.
Date 2015/02/14 17:46:01
Name 랜덤여신
Subject [일반] 러스트 프로그래밍 언어란 무엇이며, 왜 중요한가?
제가 '파이선'이라는 프로그래밍 언어를 처음 배우게 된 것은 2004년의 일입니다. 그 이후로 10년 동안, 파이선은 저의 제1언어였습니다. 제 모국어는 C++(MFC)이지만, 가장 오래 그리고 능숙하게 쓰는 언어는 파이선이죠.

파이선은 좋은 언어지만, 몇 가지 (심각한) 단점이 존재하는 언어이기도 합니다. 다른 언어로 바꾸고 싶은 마음이 자주 들던 무렵, 제 눈에 '러스트'라는 언어가 눈에 띄었고, 제 마음 속의 할 일 목록에 갈무리해 두었습니다. 그러다 지난해 말, 저는 마침내 파이선 대신 러스트를 주 언어로 써야겠다고 결심하게 되었습니다.

이 글에서는 러스트가 어떤 프로그래밍 언어인지 간략히 설명할 것입니다. 사실 간략하다고는 하나, 그것은 글의 길이가 짧다는 의미고, 내용 자체는 상당히 어려울 수도 있습니다. 또한, 저 자신이 러스트에 대해 완전히 알지 못하기 때문에, 간단 명료하게 설명하지 못한 부분이 있을 수도 있습니다. 양해하고 읽어 주시면 감사하겠습니다.

----

[러스트의 역사]



러스트는 원래 Graydon Hoare가 혼자 개발하던 언어였습니다. 그러던 것이 이 언어의 잠재성을 알아 본 모질라가 2009년부터 스폰싱을 시작하였고, 2012년 1월에 드디어 본격적인 개발이 시작되었습니다. 따라서 Graydon이 개인적으로 개발한 기간까지 합치면 8년 가까이, 본격적인 개발이 시작된 후부터 따지면 3년이 넘는 기간 동안 개발된 언어라고 할 수 있겠습니다.

모질라는 웹 기술의 주도자답게, 러스트를 이용하여 웹 브라우저를 다시 짤 생각을 하고 있습니다. 이를 위한 시험 프로젝트로 'Servo'라는 브라우저 엔진이 개발되고 있으며, 이미 Gecko의 3배 정도로 빠르다고 합니다.

----

[왜 러스트인가?]

C나 C++에서 프로그래밍을 하다 보면 자주 만나게 되는 것이 '이 프로그램에서 잘못된 연산을 수행'(segmentation fault)하는 오류입니다. 메모리 관리를 프로그래머가 직접 하다 보니 실수를 하게 되는 거죠. 대표적인 경우로 use-after-free(free()한 다음에 메모리를 사용하는 문제) 그리고 double-free(한 메모리를 두 번 free()하는 문제)가 있습니다. 이 실수가 좋게 끝나면 그냥 프로그램이 죽는 거고, 안 좋게 끝나면 온갖 보안 버그가 발생합니다. 그런데 사람이다 보니 실수를 아예 안 할 순 없죠.

그래서 자바를 필두로 파이선, PHP, C# 같은 오늘날의 프로그래밍 언어들은 '쓰레기 수집'(garbage collection)이라는 기능을 갖추고 있습니다. 이러한 언어를 사용하면 메모리 관리를 프로그래머가 직접 할 필요가 없습니다. 사실, 이들 언어에는 free() 자체가 없습니다. 하고 싶어도 못 하죠.

하지만 쓰레기 수집에 장점만 있는 것은 아닙니다. 다음과 같은 단점이 있죠:

- 비용: 쓰레기 수집은 [공짜가 아닙니다.] 어떤 객체 때문에 메모리를 필요로 할 때, 프로그래머는 이 메모리를 정확히 언제 반환해야 하는지 알지만, [컴퓨터는 모릅니다.] 그래서 컴퓨터는 주기적으로 객체가 아직 필요한지 아닌지 확인하거나(tracing GC), 아니면 포인터가 생성되고 제거될 때마다 참조 횟수를 확인해야 합니다(reference counting). 두 방법 모두 CPU와 메모리를 사용하며, 따라서 [프로그램 수행 속도가 느려집니다.]

- 멈칫멈칫: 쓰레기 수집이 일어나는 동안에는 [프로그램의 실행이 잠시 멈추게 됩니다.] 대부분의 경우 큰 문제는 없지만, 몇몇 사용례에서는 문제가 될 수 있습니다. 예를 들어 무인 자동차를 만들었는데, 쓰레기 수집을 하느라 [브레이크를 제때 밟지 못하면] 큰 사고가 나겠죠. 밀리초 단위로 온도가 왔다갔다하는 핵 발전소는 어떨까요. 하다못해 FPS 게임이나 리그 오브 레전드만 하더라도, 스킬을 썼는데 [랙 때문에 제때 안 나가면] 부모님 안부를 묻게 될 것입니다. 안드로이드가 과거 느릿느릿하게 느껴졌던 것도 이와 관련이 있습니다. 사람이 느끼는 '빠른 GUI'란, 무언가를 눌렀을 때 어떨 때는 빠르게 반응하고 어떨 때는 느리게 반응하는 것이 아니라, 항상 평균적인 빠르기로 반응하는 것이기 때문이죠. 이를 '예측 가능성'(predictability)이라고 합니다. 물론 수십 년 동안의 노력 덕분에 오늘날의 쓰레기 수집 알고리즘은 상당히 정교하지만, 그럼에도 이 문제를 완전히 해결하진 못했습니다.

- RAII: 메모리의 수명과 자원의 수명을 일치시키려는 'RAII'라는 원칙이 있습니다. 일부 쓰레기 수집 알고리즘에서는 메모리가 해제되는 시점이 불명확하기 때문에 이러한 원칙에 위배됩니다. 그래서 이들 언어에서는 반드시 .close()를 호출해 줘야 하죠. 혹시라도 잊게 되면 사단이 납니다.

따라서 성능이 소중한 몇몇 분야에서는 여전히 메모리 안전성이 떨어지는 C/C++를 사용할 수밖에 없었습니다. 대표적인 분야가 게임이나 운영 체제죠. 물론 덕분에 온갖 보안 버그도 같이 따라왔습니다. C/C++에서 발생하는 보안 버그들 중 절대 다수는, 알고리즘에 근본적인 문제가 있다기보다는, 순전히 프로그래머가 실수로 메모리 관리를 제대로 하지 못했기 때문에 발생합니다. 자바나 파이선처럼 쓰레기 수집이 되는 언어를 사용했다면 벌어지지 않았을 일이죠. 그러나 성능 때문에 C/C++를 포기할 수도 없었습니다.

그래서 나온 것이 러스트입니다. 러스트는,

- 쓰레기 수집이 필요 없는 메모리 안전성(memory safety without garbage collection)

을 갖추고 있습니다. 이는 러스트의 가장 큰 특징이자, 연구 언어가 아닌 상용 언어들 중에서는 유일하기도 합니다. 기존의 언어는 메모리 안전성이 없거나, 아니면 쓰레기 수집을 사용하기 때문이죠. 러스트는 대체 어떻게 이런 일을 실현했을까요?

이를 위해 러스트에는 '소유권'(ownership)이라는 개념이 있습니다. 어떤 객체를 가리키는 포인터는 여러 개가 있을 수 있지만, 그 중 오직 하나만 소유권을 가지고 있습니다. 또한 소유권이 있는 포인터만 객체의 내용을 바꿀 수 있습니다. 마지막으로, [소유권을 가진 그 포인터가 사라질 때, 메모리도 해제됩니다.] 가장 중요한 것은 이러한 확인 과정이 컴파일 시간에 이루어진다는 것입니다. 즉, 쓰레기 수집이 동반하는 런타임 오버헤드가 러스트에는 없습니다. 이 규칙에 어긋나는 코드가 발견되면(즉, 메모리 안전성을 침해할 만한 코드가 발견되면), 컴파일 오류가 납니다.

예를 들어, 다음과 같은 C++ 코드를 보겠습니다:


std::vector niko;

niko.push_back("니코니코니");

std::string &a = niko[0];
std::cout << a << std::endl;

niko.push_back("아나타노 하-토니");

std::cout << a << std::endl;


이 코드는 프로그래머의 의도대로라면 '니코니코니'를 두 번 출력해야 했을 것입니다. 하지만 실제로는 한 번만 출력되고, 두 번째 시도에서는 '잘못된 연산 수행 오류'(segmentation fault)가 납니다. 이는 niko에 두 번째 아이템이 추가되는 순간, 내부적으로 크기를 두 배로 뻥튀기한 배열을 만들고 기존 원소들을 새로 만들어진 배열에 옮겨 담기 때문입니다. 그래서 기존에 a가 가리키고 있던 메모리 주소는 무효화되고, a는 소위 말하는 'dangling pointer'가 되는 것이죠.

비슷한 일을 하는 코드를 러스트로 쓰면 다음과 같고:


let mut lyrics = vec![];

lyrics.push("쏴버려! 마음 속에 새긴 꿈을");

let a = &lyrics[0];
println!("{}", a);

lyrics.push("미래조차도 내버려 둔 채");

println!("{}", a);


이 코드는 컴파일 오류가 납니다.


test.ru:9:5: 9:11 error: cannot borrow `lyrics` as mutable because it is also borrowed as immutable
test.ru:9     lyrics.push("미래조차도 내버려 둔 채");
              ^~~~~~
test.ru:6:14: 6:20 note: previous borrow of `lyrics` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `lyrics` until the borrow ends
test.ru:6     let a = &lyrics[0];


오류 메시지에 대해 자세히 설명하진 않겠습니다만, 대충 훑자면 두 번째 아이템 추가 시도가 거부된 것을 알 수 있습니다. 그리고 그 이유는, 배열에 아이템을 추가하려면 배열에 대한 소유권을 얻어야 하는데, 그 소유권을 이미 a가 가지고 갔기 때문입니다. 따라서 배열에 아이템을 더 추가하기 전에 a가 기존 소유권을 반납해야 합니다.

----

러스트의 가장 핵심적인 부분을 설명하였으니, 이제 그외 잡다한(?) 특징들을 나열해 보겠습니다. 우선, 장점부터:

[장점]

- 비용이 안 드는 추상화(zero-cost abstractions)

러스트는 파이선이나 자바, 하스켈 등 고수준 언어처럼 높은 수준의 추상화를 제공합니다만, 이것을 비용이 안 드는(최소한, 적게 드는) 방향으로 달성합니다. 위의 메모리 안전성이 한 예입니다. 사람들이 C의 뒤떨어지는 추상화를 욕하면서도 계속 쓰게 되는 것은 C를 성능으로 대적할 만한 언어가 여전히 없기 때문입니다. 자바가 다년간의 노력 끝에 가장 근접해 있지만, 아직도 2배 가까이 느립니다. 하물며 파이선 같은 것은 C의 수십 배까지도 느립니다. 예전에 콩식 생성기를 여러 가지 언어로 구현하면서 성능을 비교해 본 적이 있는데, 그때 결과는 이랬습니다:


C: 1.1s
Rust: 1.2s
C (unoptimized): 1.6s
Java: 3.7s
Haskell: 5.5s
Rust (unoptimized): 6.6s
JavaScript (with Node.JS): 9.3s
PyPy: 9.9s
JavaScript (with Chrome): 10.6s
JavaScript (with Firefox): 12.9s
Python: 69.8s
PHP: 107.2s


물론 콩식 생성기는 매우 특수한 목적의 프로그램이므로 위 통계가 결코 과학적인 분석이거나 한 것은 아닙니다만, 순수한 계산 성능 면에서만 보면 C 및 러스트와, 여타 언어들 사이에는 여전히 좁히기 어려운 격차가 있는 것을 알 수 있죠.

특히 러스트가 신생 언어이고, 앞으로 컴파일러의 발전에 따라 더 빨라질 가능성이 있다는 것을 생각해 보면, 최소한 성능 면에서는 러스트의 전망이 무척 밝다고 할 수 있겠습니다.

- C와 호환이 쉽다

러스트는 C++와 유사하게 extern "C" 를 써서 C 호환 인터페이스를 제공할 수 있습니다. 따라서 다른 언어에서 러스트 코드를 호출하기가 쉽습니다. 또한 러스트에서 C 코드를 호출하는 것도 무척 간단합니다. 파이선처럼 ctypes 같은 모듈을 쓰면서 난리 법석을 피울 필요가 없습니다. 쓰레기 수집하는 언어들끼리는 메모리 모델이 서로 달라서 연동하는 게 매우 어렵기도 한데, 러스트는 쓰레기 수집하는 언어가 아니므로 이 경우에도 해당하지 않습니다.

- 문법

러스트의 문법은 C/C++/자바/PHP처럼 'C 계열'입니다. 즉 {, }, <, > 같은 것들을 씁니다. 이는 러스트의 공략 대상층이 C/C++ 사용자라서 그렇습니다. 친숙한 문법을 사용해야 기존 사람들이 적응하기 쉽겠죠.

- C++보다 간단하다

C++가 지금처럼 복잡한 언어가 된 이유는, 하위 호환성을 유지하면서 온갖 개념을 무차별적으로 받아들였기 때문입니다. implicit copy/move constructor 같은 게 그렇죠. 이런 규칙을 일일이 외우고 쓰기란 무척 어렵습니다. 러스트는 하위 호환성을 고려할 필요가 없었기 때문에 (최소한 지금으로서는) 언어가 C++보다는 무척 간결합니다. 특히 C++11의 move semantics, r-value references 같은 것들이 러스트에서는 기본값이기 때문에 참 좋습니다.

- Concurrency

모질라가 러스트를 바탕으로 Servo를 만들게 된 이유이기도 합니다만, 러스트는 오늘날 많은 언어처럼 concurrency를 위한 여러 가지 기능을 제공합니다. '채널'도 물론 있고요. 그 중 재밌는 기능이, 여러 스레드에서 하나의 변수를 변경하는 코드를 생각 없이 짜면, 위에서 말한 memory safety 기능과 마찬가지로, [컴파일 오류]가 난다는 것입니다. 멀티스레드에서 발생하는 온갖 문제 중 대표적인 것이 data race라고 생각해 볼 때, 이 기능 하나만으로도 상당한 버그를 잡는 데 도움이 됩니다.

- 뒷받침하는 기업이 있다

프로그래밍 언어란 혼자 만들 수도, 여럿이서 만들 수도 있지만, 아무래도 월급을 받는 전업 개발자가 투입되는 게 개발 속도가 더 빠르기 마련입니다. 러스트는 현재 [모질라에서 스폰싱]하고 있으며, 삼성도 일부 지원하고 있습니다. 러스트만 전업으로 개발하는 개발자가 8명 정도 있고요. 파이선 같은 경우 완전히 커뮤니티의 힘만으로 만들어졌기에 지금의 위치에 오르는 데 상당한 시간이 걸렸지만, 러스트는 훨씬 사정이 나을 것으로 보입니다.

- 기타

그밖에 패턴 매칭, algebraic data types, 타입 추론 같은 게 있지만 자세한 설명은 생략합니다.

----

이제 단점을 알아보면:

[단점]

- 이름

러스트(Rust)... 영어로 '녹'이라는 뜻입니다. 하도 많은 이름 중에 왜 하필 녹슬었다는 것인지 모르겠습니다. 잘 짠 파이선 코드를 흔히 '파이서닉'(Pythonic)하다고 하는데, 상당히 간지 나는 표현입니다. 그런데 잘 짠 러스트 코드는 현재 '녹슬었다'(Rusty)고 불리고 있습니다. 재밌긴 한데 영 슬픈 표현입니다.

게다가 러스트는 게임 러스트와도 이름이 겹칩니다. 가끔 러스트 개발자 커뮤니티에 러스트 게임 관련 질문이 올라오기도 하거든요.

- 컴파일러와의 사투

앞서 말한 것처럼 러스트의 컴파일러는 코드를 분석하여 잠재적으로 메모리 안전성을 침해할 만한 코드가 있으면 컴파일 오류를 냅니다. 그런데 가끔은 이게 너무 과해서 정상적인 코드처럼 보이는데도 컴파일러가 거부하는 경우가 있습니다. 95%의 경우 이것은 프로그래머가 실수로 잘못 짠 것입니다만, 나머지 5%는 컴파일러가 지나치게 보수적이기 때문에 발생하는 컴파일 오류입니다. 이 경우 다른 방식으로 짜거나, C#의 unsafe 블럭과 비슷한 구문을 동원해야 합니다.

- 문법

C/C++/자바/PHP 같은 'C 계열' 언어와 문법이 비슷하다는 것은 장점이지만, 단점도 됩니다. 파이선이나 하스켈 같은, 중괄호를 쓰지 않는 언어의 문법적 아름다움이 러스트에는 없습니다. 코드 전체가 특수 문자로 점칠되어 있습니다. 파이선을 주로 쓰던 저로서는 아쉬운 점입니다.

- 자바나 파이선, 루비보다 복잡하다

러스트가 C++보다 쉬운 건 사실이지만, 여전히 자바나 파이선 같은, 쓰레기 수집을 사용하는 자동 관리되는(managed) 언어들보다는 어렵고, 알아야 할 게 많습니다. 특히 러스트의 메모리 관리 메커니즘은, C/C++를 쓰면서 메모리 관리가 얼마나 골치 아픈 것인지 아시는 분들에게는 충분히 어필할 만하지만, 그렇지 않은 분들에게는 쓸데없이 복잡하게만 느껴질 것입니다.

----

언어는 종교와도 같기에 서로 다른 두 언어를 비교하는 것은 논란을 많이 일으키는 일이기도 합니다. 하지만 제 경험에만 의존하여, 순전히 주관적인 의견을 내놓자면 이렇습니다:

C++와의 비교

C++는 온갖 일들을 할 수 있는 언어지만, 그 대가로 엄청나게 복잡해졌습니다. C++11, C++14 등이 나오면서 훨씬 나아진 것은 사실이지만, 알아야 할 것은 더 많아졌고, 수많은 지뢰들을 밟지 않고 C++를 쓰는 것은 여전히 어렵습니다. 결정적으로 C++는 러스트만큼의 memory safety를 제공하지 않습니다.

파이선과의 비교

이 부분이 제가 중점적으로 생각한 부분입니다. 잘 쓰던 파이선을 버리고 러스트로 넘어갈 만한 충분한 이유를 발견해야, 러스트를 배우는 데 시간을 투자하는 것에 대한 자기 합리화를 할 수 있으니까요.

우선, 저는 파이선의 동적 타입에 지치기 시작했습니다. 동적 타입은 프로그래머에게 상당한 자유를 부여하지만, 변수 이름 하나 고칠 때마다 런타임에 무언가 오류가 나지 않을까 노심초사하는 것은 이제 싫증이 납니다. 그보다는 깔끔하게 컴파일 오류가 나는 게 행복할 것 같습니다.

또한, 때때로 파이선을 쓰면서 나쁜 성능 때문에 고통을 받을 때도 있었습니다. 쿨하게 서버를 더 붙여 버리는 수도 있지만, 그것도 다 비용이고, 결정적으로 몇몇 경우에는 horizontal scaling이 불가능한 경우도 있습니다. 한 예로, 위키백과의 경우 최근 PHP의 느린 속도를 개선하고자 페이스북의 PHP 컴파일러를 도입했고, 페이지 반응 속도를 두 배로 빠르게 할 수 있었습니다.

러스트가 파이선보다 어렵고 코드 줄 수도 늘어나는 건 사실이지만, 빠르고 효율적인 프로그램을 짤 수 있다면 감수할 만한 가치가 있다고 생각합니다.

하스켈과의 비교

파이선을 버리고 옮겨갈 언어로 지난 수 년 동안 고려했던 또 하나의 언어는 하스켈입니다. 하스켈은 순수 함수형 언어로서, 새롭고 참신한 개념을 많이 도입해 왔고, 성능 또한 준수합니다. 그러나 결국 주 언어로 선택하지 않았던 이유는:

하스켈은 함수형 언어고, 기존 프로그래머들이 익숙한 절차형 언어랑은 많은 부분에서 다릅니다. 그래서 하스켈을 배우는 건 상당히 어렵습니다. 반면에 러스트는 일단 절차형 언어고, C 계열 문법을 사용하기 때문에 상대적으로 배우기 쉽습니다. 저 혼자 짜는 프로그램이라면 상관이 없겠지만, 주위 동료들이 하스켈을 쓸 수 없다면, 여럿이서 하는 프로젝트에 하스켈을 적용하기는 여전히 어려움이 있다고 생각했습니다.

하스켈의 성능이 괜찮긴 하나 여전히 zero-cost abstractions이라고 부르기에는 어려움이 있습니다. 일단 하스켈 자체가 쓰레기 수집 언어인데, 함수형 언어들의 immutability한 특성 때문에 쓰레기가 무척 많이 발생합니다. 이는 메모리(I/O) 관련 성능이 떨어진다는 의미이기도 합니다. 그래도 위의 콩 벤치마크에서 보듯이 파이선보다는 훨씬 빠르지만, C/C++와 비교하면 다소 아쉽습니다.

----

지금까지 언급한 저의 '러스트 찬송가'가 사실에 가깝다고 해도, 잘 돌아가는 코드베이스를 버리고 러스트로 다시 짜는 것은 어지간한 모험심이 없으면 시도하기 어려운 일이겠죠. 특히 기존 코드베이스가 수백만 줄이라면 모든 것을 다시 짜는 건 불가능에 가깝습니다.

따라서 저의 제안은, 기존 프로젝트의 새로운 모듈만이라도 러스트를 도입해 보면 어떨까 하는 것입니다. 러스트의 C 호환성 때문에 다른 언어와 결합하는 것은 상대적으로 쉽습니다. 완전히 새로운 프로젝트를 시작할 일이 있다면 러스트를 고려해 보는 것도 괜찮겠고요.

물론 언어를 선택하는 데는 언어 자체의 완성도뿐만 아니라 수많은 고려 사항들이 있습니다. 개발 도구의 성숙도, 라이브러리 생태계, 주변에 도움을 요청할 사람이 많은가 여부 등...

그럼에도 불구하고 저는 러스트에서 상당히 큰 가능성을 보고 있습니다. 지금까지 C/C++를 대체하겠다고 수많은 언어가 나왔지만, 쓰레기 수집으로 인한 성능 저하와, 실시간성을 달성하기 어렵다는 문제(특히 임베디드에서는 이게 필수죠) 때문에 완전히 대체하지 못했습니다. 과연 러스트는 성공할 수 있을까요?

1.0 버전 출시가 5월 15일로 정해진 지금은 러스트를 배우기 최적의 때입니다. 어떤가요, 한번 도전해 보고 싶지 않으신가요?

http://www.rust-lang.org/

통합규정 1.3 이용안내 인용

"Pgr은 '명문화된 삭제규정'이 반드시 필요하지 않은 분을 환영합니다.
법 없이도 사는 사람, 남에게 상처를 주지 않으면서 같이 이야기 나눌 수 있는 분이면 좋겠습니다."
15/02/14 18:11
수정 아이콘
좋은 글 감사합니다. 컴파일 단계에서 메모리 참조 오류를 잡는다니... 혁신적인 언어군요.
대신 지적하신대로 엄청 빡세겠군요.
포인터 배열 같은 건 아예 못쓸 듯 하네요.
랜덤여신
15/02/14 19:56
수정 아이콘
포인터 배열도 만들 수 있습니다. 다만 본문에서 언급한 것처럼 항상 한 번에 하나의 포인터에만 소유권이 있고, 그 포인터만 객체의 내용을 바꿀 수 있게 되죠. 예컨대 포인터 A, B, C를 포인터 배열 D에 넣고 관리한다면 D가 소유권을 반납하기 전에는 A, B, C를 쓸 수 없게 되겠지요.

물론 이러한 제한도 unsafe 블럭을 쓰면 해제가 가능하지만요. unsafe 블럭을 쓰는 건 나쁜 게 아니고, 표준 러스트 라이브러리도 자주 이용하는 방식입니다. 러스트에서의 unsafe 블럭이 그 블럭만 메모리 안전하지 않다는 뜻이기 때문에 그 부분만 집중적으로 코드 리뷰를 하면 되는 반면에, C/C++는 모든 코드가 unsafe 블럭인 것이나 다름 없으니까, 설령 unsafe 블럭으로 점칠된 러스트 코드이더라도 C/C++ 코드보다는 메모리 버그를 잡기가 훨씬 쉽지요.
Neandertal
15/02/14 18:11
수정 아이콘
91년도 제가 대학 1학년이었을 때 제 포트란 숙제를 대신해주곤 했던 서울 친구가 생각나네요. 지금은 어디서 무얼 하고 있을지. 포트란도 못했는 데 다른 언어가 될리가...--;;
15/02/14 18:18
수정 아이콘
전 2015 년에도 포트란으로 일하고 있습니다...
Neandertal
15/02/14 18:48
수정 아이콘
포트란이 아직도 쓰이는군요...포트란은 좋은 언어였네요...--;;;
자바초코칩
15/02/14 18:49
수정 아이콘
와... 대단하십니다.. 덜덜;;
Arya Stark
15/02/14 18:13
수정 아이콘
이번에 대학원을 가는데 공부를 손에 놓은지 오래 되어서 C를 좀 손을 다시 대려면 공부가 필요한데

공부하는 김에 같이 한번 봐야겠네요. 감사합니다.
The Genius
15/02/14 18:16
수정 아이콘
오래 된 언어들을 주로 사용해 본 입장에서는 참 어려 보이는 언어네요. 재미있게 잘 읽었습니다.
김연우
15/02/14 18:30
수정 아이콘
c++이 메인이긴 하지만 C++이 정말 싫은게,

내가 알아서 할께. 책임도 질께 -> python, java
니가 하란데로만 해. 니가 책임저 -> C
라면, C++ 은

내가 알아서 할께. 그런데 책임은 니가 저.
란 느낌이라서요.

Template과 operator overloading이 꼬이면 당췌 파악이 안되니.


비슷한 이유로 golang을 쓰고 있습니다. 성능도 괜찮고, 문법도 간단하고, 멀티쓰레딩을 채널로 해결하면서, cgo를 통해 c 와의 연동이 참 좋으니.
확실히 library linking만 봐도 C는 10년 후에도 남겠지만, C++은 rust가 됐건 go가 됐건 사라질 가능성이 있어보입니다.
랜덤여신
15/02/14 19:46
수정 아이콘
저는 Go 언어 자체에는 실망한 부분이 많았습니다. Go의 개발자의 이야기를 들으니, Go의 설계 철학은 갈수록 비대해지는 C++에 맞서 '최대한 적은 기능을 제공하자'는 것이라고 합니다. 이러한 기본 철학 자체에는 공감할 수 있습니다. 하지만 generics조차 없는 건 심했다고 생각합니다. generics가 없으니 interface[](중괄호가 안 쳐지네요)를 사용할 수밖에 없고, 오류 처리도 fp, err := os.Open("...") 로 점칠된 코드가 나오는 것이죠. C++의 template이 문제였다면(실제로 문제지만), 더 개선된 타입 시스템을 만들었어야 한다고 생각합니다. 그렇게 하는 대신 generics 개념 자체를 완전히 빼버리고 interface[]를 쓰라는 데는 공감할 수 없었습니다.

결정적으로 Go 또한 쓰레기 수집을 하는 언어이기 때문에 기존 C/C++이 차지하던 분야를 완전히 대체할 수가 없습니다. Go가 '시스템 프로그래밍 언어'를 표방하다가 슬그머니 그 문구를 홈페이지에서 뺀 게 이 때문이었죠. 지금은 concurrency를 중점적으로 홍보하는 것 같더군요. 이 분야에서라면 여전히 경쟁력이 있으니까요.

Go의 장점은 구글이 제공하는 다양한 개발 도구 및 라이브러리라고 생각합니다. gofmt 같은 것 말이죠. 제가 느끼는 Go는 '잘 다듬어진, [구글이 관리하는] Java'에 가깝습니다. 뭐 구글이 주도한다는 게 무척 소중한 특징이긴 하지만요...
15/02/14 20:22
수정 아이콘
말씀대로 go의 수요는 C/C++ 유저가 아니라 Java 유저에 있는 것 같습니다. 사실 Java도 C++를 단순화하겠다고 만들어진 언어였으니 비슷한 목적에서 시작한 go가 그런 종착점에 달하는건 필연인 듯 하네요. 다만 그 시점의 Java나 현 시점의 Rust에 비해 어떤 문제를 해결하는지가 명확하지 않아서 성공 여부는 아직 미묘한 듯...
김연우
15/02/14 20:45
수정 아이콘
비슷한 이유란건 'C와의 연동이 쉽다'라는 거였습니다.
메인 라이브러리는 C로 짜고, 그걸 wrapping하는 라이브러리를 후딱 만들어야 했는데... 처음에는 java로 짜려 했는데 성능이 그렇게 좋지도 않고, Jni 연결하는 부분도 그렇게 편하진 않아서요.
그런데 go는 C란 연동하기가 너무 쉬웠고, 코드도 간결하고, 성능도 좋아서 쓰게 돼었지요.
자바초코칩
15/02/14 18:49
수정 아이콘
전 임베디드 하는 사람이라 C언어 하나만 잘 하면 되고, 다른 언어 알 필요도 없지만, 흥미로운 글이네요.
개발도구 측면에서는 어떤가요?
랜덤여신
15/02/14 19:51
수정 아이콘
현재 비주얼 스튜디오용 러스트 플러그인이 있는데, 아마 알파 버전 수준이라고 생각됩니다. 아마 다른 IDE용 러스트 플러그인도 차차 개발되겠죠.

저는 Vim으로 주로 코딩하기 때문에 풀셋 개발 도구에 대한 욕구는 별로 없지만, Vim용으로도 러스트 자동 완성 플러그인이 있다고는 들었습니다.

사실 저는 파이선 쓸 때도 그냥 print()로 디버깅하던 사람이라서 ㅠㅠ 원시인이죠, 뭐...
츄지Heart
15/02/14 18:54
수정 아이콘
파이썬 배워보려고 했는데, RUST 해봐야겠습니다. Django 같은 프레임워크도 많이 나와주겠죠?
랜덤여신
15/02/14 19:38
수정 아이콘
제가 러스트에 기대하고 있는 분야도 웹 개발입니다. 지금은 Iron이라는 웹 프레임워크를 관심 있게 보고 있는데, 상당히 괜찮아 보입니다. 러스트용 웹 프레임워크 및 라이브러리를 정리해 놓은 페이지도 있습니다:

http://arewewebyet.com/
15/02/14 18:54
수정 아이콘
흥미로운 언어네요~
히라사와 유이
15/02/14 18:56
수정 아이콘
좋은글 감사합니다.
근데 C++을 적극적으로 쓰는 분야가 있나요?
제가 임베디드 쪽이라 잘 몰라서;
C++은 절차언어에서 OOP로 넘어가는 과도기에서 만들어 진거라 응용 프로그램쪽으로 가면 오히려 OOP인 자바나 C#이 나은것 같고,
임베디드쪽은 하드웨어 제한 때문에 결국 C가 대체불가인데..
개인적으로는 아직도 XP SP2쓰는. 그런데 절대로 추가로 뭔가 깔생각이 없는 클라이언트 덕에 C++로 짜본것 말고는 경험이 없어서;
15/02/14 19:09
수정 아이콘
꽤 큰 회사 다니는데 C++엄청 쓰네요... 모바일 쪽이에요
그 과도기에 만들어진 코드때문에 아직도 쓰이는 걸지도 모르겠네요... 크크크
랜덤여신
15/02/14 19:37
수정 아이콘
본문에서 말한 것처럼, C나 C++ 같은 unmanaged 언어는 주로 시간 제한이 엄격한 분야에서 쓰입니다. 게임, 운영 체제, 웹 브라우저 같은 곳이죠. 게임 같은 경우 초당 프레임 수에 목숨을 걸곤 하고, 운영 체제가 1초 느리면 다른 모든 프로그램이 10초씩 느려지기 때문에 C/C++가 필수적이고, 웹 브라우저도 요즘 성능 경쟁을 미친듯이 하기 때문에 이러한 언어가 필수적이죠. 이런 분야를 '시스템 프로그래밍'이라고 합니다. 말씀하신대로 일반 응용 프로그램은 버튼이 몇십 밀리초 늦게 눌린다고 사용자가 화를 내는 일은 별로 없기 때문에, 쓰기 어려운 C/C++보다 자바, C#, 파이선 같은 것들을 쓰는 것이라고 생각합니다.

C++가 과도기에 만들어져서 문제라기보다는, 30년이라는 긴 세월 동안 C++에 다양한 기능들이 추가되면서 난잡해졌다는 표현이 맞을 것 같습니다. 왜냐하면, C++ 자체에 객체 지향 개념이 모자라고 그러진 않기 때문입니다. 하위 호환성을 유지하느라 쓰기가 매우 번거로워져서 문제죠. 만일 C++가 쓰기 쉬웠으면 굳이 자바나 C#를 쓸 필요가 없었겠죠. 자바나 C#이 아무리 발전해도 unmanaged 언어보다는 느릴 수밖에 없으니, 개발자 생산성이 똑같이 나온다면 성능이 좋은 걸 쓰는 게 좋으니까요.

러스트는 제가 보기에 '쓰기 쉬운 C++'에 가깝습니다. unmanaged 언어이므로 성능은 C/C++만큼 빠르고요. 물론 쓰기 쉽더라도 여전히 자바나 C#보다는 어렵고, 파이선보다는 훨씬 어렵긴 합니다. 그러나 제가 느끼기에 러스트는 사용성과 성능 사이의 적절한 sweet spot을 찾은 것 같습니다.
히라사와 유이
15/02/14 20:22
수정 아이콘
오오 장문의 설명이 덜덜;;
감사합니다.
역시 제가 잘 모르는 분야에서 많이 쓰였네요.
Je ne sais quoi
15/02/14 20:07
수정 아이콘
http://githut.info/
당연히 절대적인 척도는 안되겠지만, github에서는 C++이 C보다 순위가 높습니다.
http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html
대표적인 지표중 하나인 tiobe에서도 C와 Java에 많이 뒤지지만 3위입니다.
여전히 많은 분야에서 쓰이죠. 가장 대표적인 분야가 게임쪽입니다.
15/02/14 20:24
수정 아이콘
C는 추상화 도구가 전혀 없고, 다른 언어들은 퍼포먼스 문제가 있고요. 현 시점에서 이 둘이 모두 필요한 영역에는 C++ 말고는 방법이 없습니다. 그게 어디냐 하면 ... 좀 큰 규모의 시스템 프로그래밍 영역 전부라고 보시면 됩니다.
히라사와 유이
15/02/14 20:40
수정 아이콘
아 이렇게 말씀해주시니 확 와닿네요.
감사합니다.
빈즈파덜
15/02/14 20:37
수정 아이콘
가장 쉽게 볼 수 있는 곳이 안드로이드 프레임웍쪽 소스로 C++로 된 부분이 많습니다.
우주모함
15/02/14 22:04
수정 아이콘
게임이죠.

게임은 그 특성상 속도도 그렇지만, 또 컴퓨터의 구석구석까지, (특히 메모리!) 전부 프로그램이 통제를 해야합니다.
현존하는 모든프로그램중에 운영체제 뺨칠정도로 복잡하고 고도의 기술을 요하는 영역이 게임이죠.
그래서 게임프로그래밍은 C++이 대세입니다.

프로그래머가 신경쓸 영역이 적어진다는건 그만큼 그 언어로 할수있는 일이 제한적이라는 말도 됩니다.
하지만 C++은 어려운만큼 자유도도 높죠. 게임에는 최고입니다.
(그만큼 게임프로그래머는 타 영역에 비해 프로그래머에게 요구하는 기술수준이 상당히 높습니다.)

대부분의 게임들의경우
게임내에서 사용되는 리소스들의 메모리를 할당하고 해제시키는 자원관리 클래스가
모두 C++로 손수 구현되어있습니다. 엔진에서 그런 자원관리 클래스를 제공하는경우도있고
게임을 만들면서 본인들이 구현하기도 하죠. 그리고 DirectX라던가 OpenGL등의 3D그래픽스 API도 대부분 C++로 되어있으니까
게임은 사실 C++이 최고죠. 다른언어로 물론 저런 3D API를 쓸수있는방법이 있기는 하겠지만 아마 상당히 어려울겁니다.
그냥 C++써야죠.

전 게임프로그래머이기 때문에 C++이 모든 언어중 최고라고 생각합니다.
Je ne sais quoi
15/02/14 20:03
수정 아이콘
요즘 글쓴이만 보고 읽는 글 중 랜덤여신님이 추가됐죠. 좋은 글 잘 읽었습니다.
제 경우는 rust의 존재에 대해 안 지는 몇 년이 됐지만, 관심도는 후순위입니다. 저도 현재 1순위로 사용하는 언어는 파이썬인데, 파이썬만큼 간단한 코드대비 강력한 기능을 제공하는 게 드물거든요. 특히 요즘엔 폭발적으로 늘어나는 라이브러리들 때문에 뭐가 있는지 찾아보는 것도 버거울 지경이죠. 속도도 많이 개선됐구요. 2순위는 자바입니다. 하둡때문에 안 쓸 수가 없어요. C는 주니어 사원들 가르칠 때만 조금씩 씁니다.
공부를 잘 안 하고는 있지만, 할 경우의 우선순위는 스칼라, 클로져, 하스켈 , 자바스크립트 정도일 거 같습니다. 러스트는 아무래도 좀 더 인기를 얻고 라이브러리가 풍부해져야 고려할 거 같아요. 먼저 뛰어드는 성격이 아니라서(그러니 뒤쳐진다는 ㅜ.ㅜ).
15/02/14 20:12
수정 아이콘
현재 있는 네이티브 라이브러리랑 호환되면 넘어가볼만도 하네요.
이제 포인터도 못뚫으면 해킹은 꿈도못꿀듯
PublicStatic
15/02/14 20:16
수정 아이콘
작년에 했던 플젝이 asp.net MVC c# 코드에서 c++ DLL을 호출했었는데..덕분에 오랬만에 메모리 관리에 대해서 빡시게 고민했었습니다. C# 단에서 메모리를 할당해서 unsafe로 포인터를 넘기면 그걸 c++단에서 사용하는 방식을 썼었지요. 그래서 그런지 본문 내용이 더 재미있었네요...좋은 글 감사합니다.
15/02/14 20:24
수정 아이콘
취미로 배우는거지만 어제 막 codecademy에서 파이썬을 끝냈는데..일단 계속 해봐도 되는 게 맞겠죠?..ㅠㅠ
랜덤여신
15/02/14 20:33
수정 아이콘
초보이시라면 파이선부터 보시는 것을 추천 드립니다. 러스트는 그 자체로 파이선보다 어렵기도 하고, 인터넷상에 공개된 정보도 적습니다. 파이선은 20년이 넘은 언어이므로 문서도 많고, 주변에 물어 볼 사람도 많습니다. 파이선이 제 사랑에서 멀어지긴 했지만 10년 넘게 제가 주력으로 썼고, 여전히 전 세계적으로 널리 쓰이는, 좋은 언어입니다. 두려워(?)하지 마시고 파이선 배우시면 되겠습니다.
지구사랑
15/02/14 20:42
수정 아이콘
취미로 코딩/알고리즘 공부를 하시는 분들에게는, 특히 코딩에 첫 걸음을 들여놓으시는 분들께는 파이썬이 최고라고 생각합니다.
파이썬이 속도가 중요한 경우와 프로그램이 어느 정도 이상 커지는 경우 단점이 점점 크게 느껴질 수 있습니다만,
(회사 일이 아닌, 개인 취미 수준에서) 알고리즘을 공부한다는 관점에서는 프로토 타입을 빠르게 만들어 볼 수 있다는 장점이 더 중요하니까요.
이런 저런 것들을 만들어 보다가 점점 그 이상의 것에 대한 욕구가 커지면 C, C++, C# or Java 등의 테크트리를 밟게 되는데,
사실 그 어느 지점에선가 단순 취미 수준은 넘어간다고 생각합니다.
PS) 액셀과의 연계를 위해 처음으로 코딩을 배우는 사람에게 Visual Basic을 가르쳐 보았는데, 사실 이것도 나쁜 선택은 아닌 듯 합니다만. :)
빈즈파덜
15/02/14 20:42
수정 아이콘
설명을 들어보니 컴파일러에 static analysis 기능이 들어간 컴파일러를 가진언어라고 보면 되는건가요?
이럴경우 기존 언어로 코딩후 static analysis로 코드 분석후 error레포팅이 나온곳을 수정하는 방법과 어느것이 좋을까요?
새로운 언어를 배우는것에 조금 거부감을 가지고 있는 편이라...흐흐흐
언어 문법 배우는거 말고도 다른거 할게 많아서요...ㅠㅠ
15/02/14 21:00
수정 아이콘
정적 분석이라고 하기는 약간 부정확하고요. 이 경우는 진보된 타입 시스템을 통해 더 강한 보장을 해주는 사례입니다. 이 둘 사이의 차이라면 전자는 잘못된 프로그램을 잡아내지 못할 수도 있지만, 후자는 타입 시스템이 보장하는 한도 안에서 잘못된 프로그램은 통과되지 않는다는 점 정도가 됩니다.

어느 쪽이 낫냐? 에 대해서는 의견이 갈립니다. 전자는 잘못된 프로그램을 못 잡을 수도 있지만, 잘 돌아가는 프로그램에 대해 정적 분석기가 경고를 낸다고 그 프로그램을 못 쓰진 않죠. 반면 후자는 올바른 프로그램이라는 것을 증명할 수 있지만 타입 시스템에 통과되지 않는다면 그 프로그램은 쓸 수 없습니다. 잘못된 프로그램과 제대로 된 프로그램을 완벽하게 구분해내는 방법이 있으면 참 좋겠지만, 안타깝게도 일반적인 프로그래밍 언어에 대해서 그런 방법은 존재하지 않는다는게 증명되어 있습니다.

Rust 같은 경우는 그래도 컴파일러가 보장해주는 안전성의 정도를 고려할 때, 컴파일러가 보수적으로 튕겨내는 프로그램의 비율은 생각만큼 크지 않습니다. 대부분의 경우에 대해 우회하는 실용적인 방법을 마련해둔 상황이기도 하구요.
빈즈파덜
15/02/14 21:12
수정 아이콘
러스트 컴파일러의 성능이 어느정도인지 잘 몰라서 그러는데요...러스트 컴파일러가 ok해준 프로그램이라면 memory leak이라던지 multi-thread에서 일어나는 문제가 전혀 발생이 안되는 수준인가요?
제가 써본 prevent라는 툴이 positive false를 좀 내긴해도 복잡하지 않은 구조의 memory leak정도는 정확하게 잡아주더군요~
15/02/14 21:20
수정 아이콘
기본적으로 제대로 된 타입 시스템이라면 절대로 false negative가 발생하지 않아야 합니다. 아직 수학적으로 증명한 상태까진 아니지만 기본적으로 Rust의 목표도 역시 마찬가지고요. 타입 시스템을 우회하기 위해 사용한 unsafe 블럭들이 잘못된 것이 아니라면 메모리 버그는 안 난다고 보시면 됩니다.
빈즈파덜
15/02/14 21:34
수정 아이콘
그렇군요~ 벌써 그런 수준의 컴파일러가 만들어져가는군요~~ 많은 가르침 감사합니다~^^
랜덤여신
15/02/14 21:34
수정 아이콘
수학적인 증명은 아직이지만, 이론상 러스트 컴파일러가 OK한 코드라면 메모리 버그는 '전혀' 없어야 정상입니다.

멀티스레드는 사정이 좀 다른데, 러스트에서 보장하는 것은 data race 방지입니다. 물론 이것만으로도 상당한 것이지만, 교착 상태(deadlock) 같은 것은 러스트로도 막을 수 없습니다.
랜덤여신
15/02/14 21:30
수정 아이콘
네, 비슷합니다. 정적 분석이 '컴파일 경고'에 가깝다면, 러스트의 타입 시스템은 '컴파일 오류'에 가깝다고 할 수 있겠죠.

저는 정적 분석보다는 타입 시스템을 이용하는 편이 낫다고 생각합니다. 정적 분석은 100% 확실하진 않기 때문입니다. 만에 하나 정적 분석기가 놓친 게 있을까봐 밤잠도 설치면서 불안감에 떨지도 모릅니다...까지는 아니고, 그냥 좀 깔끔하지 않은 느낌이 들어서요. 저는 C/C++ 쓸 때도 모든 경고 켜고, 경고가 하나라도 있으면 컴파일 실패하는 옵션 켜놓고 했습니다. 하하
빈즈파덜
15/02/14 21:37
수정 아이콘
금융이나 서버쪽 관련 일을 하시나보네요...흐흐흐
저는 버그 한두개 있어서 critical한 문제가 발생하지 않는 분야에서 일하다보니 가끔 컴파일 warning은 무시합니다..흐흐흐
근데 일정이 항상 쪼달려서 문제지요~ㅠㅠ
15/02/14 21:06
수정 아이콘
여담이지만 Rust에서 제공하는 개념 중 상당 부분은 C/C++ 전문가들 사이에서 암묵적으로 널리 쓰이던 유용한 패턴들을 명시적으로 프로그래밍 언어의 영역에 편입한 사례들이라 한번 배워두시면 C/C++에서도 유용하게 쓰실 수 있을겁니다. OOP로 코딩하던 자바 고수가 C를 쓴다고 코드가 절차 지향적 시대로 퇴행하지 않는 것처럼요.
15/02/15 10:42
수정 아이콘
흠 전 어차피 c++를 일에서 계속 써야 해서 관심있는 부분은 이건데, 혹시 어떤거 말씀하시는 것인지 예를 들어주실 수 있으신가요?
15/02/15 13:07
수정 아이콘
Rust에서 가장 핵심이 되는 ownership/lifetime 같은 개념이 좋은 사례가 될 수 있겠네요. 이 부분은 제가 열심히 설명한다고 해도 잘 와닿지는 않을 것 같고요. unique_ptr/shared_ptr 등을 쓰면서 재미를 보셨다면 Rust를 사용하는 경험이 분명히 크게 도움 되실겁니다.
15/02/16 04:02
수정 아이콘
답변 감사드립니다!
랜덤여신
15/02/15 22:49
수정 아이콘
저는 C++의 오랜 경험으로 탄생한 move semantics와 r-value references가 러스트에서는 기본값이 되었다는 것을 지적하고 싶군요.

C++에는 원래 copy constructor만 있었습니다. 그러다가 시간이 흐르면서 C++ 개발자들은 이 패턴이 불필요한 복사를 많이 유발한다는 것을 깨달았죠. 그래서 C++11에서 추가된 것이 move constructor입니다. 그래서 기존에

A(const A& another) // copy constructor

만 쓰던 것이, C++11부터는

A(A&& another) // move constructor

도 쓰게 되었고, 이를 이용하여

A& operator=(A another) [ // copy and swap idiom
std::swap(data, another.data);
return *this;
]


식으로 써서 copy와 move를 동시에 지원하려는 패턴이 소위 '모던 C++'로 인정 받게 되었죠.

출처: http://stackoverflow.com/questions/3106110/what-is-move-semantics

그러나 C++를 쓰는 사람이 충분히 주의를 기울이지 않으면, 이러한 move semantics의 혜택을 누릴 수가 없었고, 불필요한 복사는 여전히 일어나게 됩니다.

그래서 러스트는 과감하게 copy constructor를 빼버렸습니다. 러스트에서 복사는 항상 명시적으로만 일어납니다.

let a = A::new(); // 새 객체 생성
let b = a; // C++라면 복사가 일어났겠지만, 러스트에서는 이동입니다.
println!("[]", a); // 이건 컴파일 오류입니다. a는 이미 이동됐기 때문입니다. a라는 변수에는 아무 것도 남아 있지 않습니다.

fn hello(a: A) [ ... ] // 함수 hello는 포인터가 아닌 값을 인자로 받습니다. (pass by value)
hello(a); // 함수 hello를 호출할 때, 이렇게 넘기면 a는 이동으로 넘어갑니다.
println!("[]", a); // 그래서 이 역시 컴파일 오류입니다. a는 이미 hello 함수 안으로 이동됐기 때문이죠.

let c = a.clone(); // 이래야 복사가 일어납니다.
hello(a.clone()); // 이렇게 해야 a가 복사로 넘어갑니다.

즉, C++에서 복사보다는 이동이 선호되는 것을 오랜 경험으로 깨달았고, 그것을 C++11에서 move semantics와 r-value reference라는 형태로 기능 추가를 하였지만, 여전히 하위 호환성 때문에 기존의 implicit 복사 생성자도 여전히 존재하죠. 따라서 '패턴'이라는 이름으로 새로운 코딩 습관을 강제하게 되는데, 이 새로운 패턴을 기존 C++ 개발자들이 잘 받아들이면 다행이지만("모던 C++"), 그렇지 않은 경우도 상당하며, 받아들였다고 하더라도 실수로 기존 패턴을 써버리는 경우도 있습니다.

반면에 러스트는 그러한 하위 호환성을 고려할 필요가 없으므로 처음부터 이동을 선호하게 바꿨고, 이런 점에서 C++의 '암묵적으로 쓰이던 유용한 패턴'을 러스트가 체화한 것을 알 수 있죠.

http://stackoverflow.com/questions/24253344/move-vs-copy-in-rust
15/02/16 03:54
수정 아이콘
친절한 답변 감사합니다. 많은 도움이 되었습니다.
azurespace
15/02/14 21:58
수정 아이콘
C나 C++에서 메모리 오류 발생시엔 이게 대체 어디가 잘못된 건지 단시간에 찾아내기란 거의 불가능합니다. 재현이 어려운 문제이기까지 하면 그냥 불가능이죠. 정적 분석에 코드 커버리지 분석에 뭐 온갖 방법을 동원하고도 못 찾을 수 있습니다.

러스트로 작성한 프로그램도 메모리 오류는 발생할 수 있습니다만 unsafe로 명시적으로 지정된 부분만 디버그하면 된다는 점은 매우 매력적이지요. 삼성이나 다른 기업들이 괜히 관심을 갖는 게 아닙니다.

컴파일러의 발전 덕분이라면 C나 C++에도 컴파일러에 그런 기능을 도입하면 되지 않냐 하시겠지만 그것이 가능하다면 진즉 했겠지요. 러스트는 코딩 시에 제약을 약간 더 거는 대신에 정말 문제가 되는 상황만은 피하기 위한 설계입니다. 그래서 러스트의 라이프타임 개념을 이해하지 못한 개발자는 당연히 되겠거니 생각하는 코드가 컴파일이 안 되어서 당혹스러워하는 경우가 자주 발생하지요. 물론 숙련이 되면 될수록 그런 코드를 작성하지 않을테니 점점 편해지겠지요.
빈즈파덜
15/02/14 22:31
수정 아이콘
그렇군요~ syntax가 기존 언어들은 단순 obj코드로 변환하기 위한 것이 였다면 Rust는 문제가 발생하는 로직오류를 미연에 방지하도록 더 제한을 걸어두었다라고 보면 되겠군요~ 대형 프로젝트이고 협업하는 사람이 많아지고 모듈들이 서로 연관성이 많은 곳에 굉장히 유용하게 쓸수 있는 언어가 될거 같네요~ 저도 시간되면 꼭 배워봐야겠습니다.
duinggul
15/02/14 22:38
수정 아이콘
좋은 글 잘 보았습니다.

Reference 카운팅 포인터는 비교적 크지 않은 비용으로 Dangling 포인터 문제를 해결할 수 있지만, 한가지 ( 그리고 치명적인 ) '순환참조에 의한 메모리 릭 발생 가능성 문제' 때문에 '큰 비용이 들어가는 쓰레기 수집 시스템' 의 존재 가치가 있는 거라고 생각하는데요,

본문의 '동시 소유권 1개 제한 규정' 만으로는 순환참조를 걸러낼 수 없을 것 같은데.. Rust에서 별도로 순환참조를 방지하기 위한 시스템이 있는지요?

그게 아니라 그냥 'Reference 카운팅 포인터 사용에 의한 부하' 만 없애주는 데 의의가 있는 거라면, '그로인해 얻게되는 성능'이 '소유권 제한 규정 에 의한 불편함'을 감수할 만한 가치가 있는 것인지에 대해서는 회의적인 느낌이라서요..
랜덤여신
15/02/14 22:48
수정 아이콘
safe한 러스트 코드만으로는 순환 참조를 [아예 만들 수 없습니다.] 그런 데이터 구조를 만들려면 두 가지 방법이 있습니다.

1. unsafe 블럭을 사용합니다: C/C++에서 하던 대로 [수동으로] 순환 참조를 관리합니다. 보통 unsafe 블럭을 쓰되, safe한 인터페이스(메서드)를 제공하는 형태로 만들곤 합니다. 표준 라이브러리의 doubly-linked list 같은 게 이런 식으로 만들어져 있습니다.

2. weak 레퍼런스 타입을 사용합니다: 러스트는 '레퍼런스 카운티드 타입'도 [라이브러리 형태로] 제공하고 있습니다. 레퍼런스 카운팅을 사용하는 다른 언어에서 weak 레퍼런스 타입을 제공하듯이, 러스트도 std::rc::Weak이라는 weak 레퍼런스 타입이 있습니다. 물론 이것도 라이브러리이며, 내부적으로는 1번과 마찬가지로 unsafe 블럭으로 구성되어 있겠죠. 다만 역시 1번처럼 safe한 인터페이스를 제공하니까 안심하고(?) 쓸 수 있는 거겠고요. http://doc.rust-lang.org/nightly/std/rc/struct.Weak.html
duinggul
15/02/14 22:53
수정 아이콘
음 그렇다면 '동시 소유권 1개 제한 규정' 외에 뭔가 다른 제약에 의해 순환참조가 원천적으로 불가능하게 되어있는 구조인가 보군요.. 잘 알겠습니다.
azurespace
15/02/14 22:59
수정 아이콘
소유권 외에 라이프타임이라는 개념이 있습니다. 간단히 말하면 어떤 객체의 레퍼런스를 받아가려면, 레퍼런스를 받아가는 객체는 그보다 먼저 죽으면 안 됩니다. 이 규칙으로 레퍼런스 카운팅 없이도 dangling pointer 문제가 발생하지 않고요. 꼭 필요한 경우에만 reference counting pointer를 사용할 수 있습니다.

그리고 러스트가 기존의 c-like language와 명확하게 구분되는 것이 immutability가 기본값이라는 겁니다. 러스트에서 기본적으로 모든 객체는 불변 객체이고, 값을 변경하려면 이 객체가 mutable함을 명시적으로 선언해야 합니다.

그런데 여기에는 다음과 같은 규칙이 있습니다.

1. immutable한 객체에 대한 mutable burrowing 금지

2. immutable burrowing한 레퍼런스가 하나라도 살아 있는 동안에는 객체의 mutability에 상관없이 수정 불가능

3. mutable 객체에 대한 mutable burrowing은 동시에 둘 이상 존재할 수 없음.

4. mutable burrowing 중인 객체에 대해 immutable reference를 획득할 수 없음
[이런 규칙들은 참조 중인 객체가 엉뚱한 곳에서 값이 바뀌는 일을 막습니다. iterator invalidation과 같은 예기치 못한 부작용 방지]

위 규칙들은 모두 [컴파일 타임]에 검사됩니다.

간단히 말하면 레퍼런스는 여러 개 있을 수 있지만 객체의 lifetime이 끝나기 전에 사라지는 것이 보장되고요. 순환 참조를 만들 수는 있는데 값을 수정하지 못하게 됩니다. 수정하려면 반드시 unsafe 구문을 씌워야 하니 문제 발생시 감시해야 하는 scope가 크게 줄어들지요.
duinggul
15/02/14 23:42
수정 아이콘
순환참조가 만들어졌다고 했을 때, 엮여있는 객체들 중 하나라도 소멸되려고 하는 순간 lifeTime 룰에 위배되니 '생성된 모든 객체는 프로그램 종료 전에 반드시 소멸되어야 한다' 는 규칙이 있다면 ( unsafe, weak 포인터 등을 쓰지 않는 이상 ) 순환참조는 컴파일 타임에 모두 걸러지게 된다고 이해하면 되겠군요
azurespace
15/02/15 00:10
수정 아이콘
조금 다른데요. 위에 설명한 건 Lifetime 규칙과 함께 쓰이는 Burrow rule입니다.

Lifetime 규칙의 개념 자체는 간단합니다. 음... 뭐랄까요. Rust의 모든 중괄호 블록은 그 자체로 C언어의 함수 스코프 같은 걸 갖습니다. 어떤 객체가 중괄호 안에서 선언되었으면 이 중괄호가 끝나는 순간 객체의 Lifetime도 끝나고 메모리는 자동으로 반환됩니다. 이렇게 함으로써 블록 안에서 선언된 객체는 블록 밖에서 접근할 수 없고, Burrow rule에 의해 이 객체의 reference 또한 블록 바깥으로 나갈 수 없죠. 어떤 변수가 자신이 선언된 블록 바깥으로 나갈 수 있는 방법은, 그 블록의 return value로 나가서 다른 변수로 명시적으로 binding되는 방법 뿐입니다. (여기에서 우리는 Rust에서 중괄호 블록 자체가 statement가 아닌, 값을 가진 expression이라는 사실 또한 알 수 있지요.) 이 경우에는 이 객체의 lifetime은 새 변수의 lifetime까지 확장됩니다. 이 변수 또한 자신이 속한 block이 끝날 때 lifetime이 끝나고... 뭐 이런 식으로 연속적으로 계산할 수 있고요.

C에서 지역 변수의 포인터를 외부로 반출하지 말라는 것과 비슷한 원칙이라고 보실 수 있겠습니다. 단지 그것을 Rust는 컴파일러가 알아서 똑똑하게 우아한 방법으로 처리해 줄 수 있습니다.

물론 이건 개념적인 것이고 실제로는 컴파일러가 Return에서 발생하는 불필요한 Move나 Copy를 제거하기 때문에 성능에 영향은 없습니다.
랜덤여신
15/02/14 23:01
수정 아이콘
성능에 대해서라면, 확실히 어떤 분야에서는 레퍼런스 카운팅으로 인한 부하를 신경 쓰지 않아도 괜찮다고 생각합니다. 그런 곳에서라면 C++의 일반적인 포인터 및 레퍼런스 대신 std::shared_ptr과 std::weak_ptr로 떡칠(?)을 함으로써 상당한 수준의 메모리 안전성을 달성할 수도 있겠지요.

하지만 그 경우에도 본문에서 언급한 것과 같은 iterator invalidation 문제는 해결할 수 없습니다.

또한 결정적으로, safe by default와 unsafe by default는 상당히 다른 느낌이라고 생각합니다. 러스트는 (불편을 감수하고) 거의 모든 코드가 safe하되 일부 unsafe 블럭만 눈여겨 보면 되는 것이라면, C++는 (편하긴 하지만) 아무리 std::shared_ptr를 쓰고 std::weak_ptr를 쓰더라도 만에 하나 삐끗해서 코드 어딘가에 일반 포인터/레퍼런스를 써버렸다면 잠재적인 버그의 근원이 되는 것이거든요. 이 차이는 크다고 개인적으로 생각합니다.

사람은 메모리 안전한 코드를 짜는 데 별로 재능이 없고 실수 투성이라는 것을, 러스트로 코딩하다 보면 알 수 있습니다. 컴파일러와 싸워야 한다는 것 자체가, 사람이 그런 식으로 사고하지 못하기 때문이거든요. 하하. (물론 러스트 컴파일러가 필요 이상으로 엄격한 면은 있습니다만, 대부분은 사람이 잘못한 거니까요) 그래서 불편함을 무릅쓰고 기계에게 맡기는 것이지요. (이것은 기계의 인류 정복의 서막...!)
azurespace
15/02/14 23:15
수정 아이콘
적어도 사람은 실수를 하지만 컴퓨터는 실수하지 않는다고 믿을만하니까요. C++이 나오기 전에 러스트가 나왔다면 세계는 지금보다 더 평화로운 곳이 되었을텐데...(반진반농)

러스트 컴파일러가 때로 추론을 잘못하는 버그가 나오기도 하지만 그것조차도 사람이 컴파일러를 잘못 짜서 그런거지 러스트의 문제가 아니죠(?) 아니면 충분한 힌트를 주지 못한 사람이 잘못한거거나(?)

아무튼 언어가 발전하는 속도가 무시무시합니다. 이제 곧 정식버전이 나오는 막바지라서 그런지 하루하루 문법이 확확 바뀌고...
잿빛토끼
15/02/14 23:47
수정 아이콘
지금 잠깐 만져보고 왔는데 신기한 녀석이네요.
변수 선언과 대입부터 응??? 이런느낌이구요.
그런데 생각해보니 이런 방식이 추후 오류는 확실하게 없을 것 같은 느낌이구요.
그런데 어렵네요.
15/03/08 21:20
수정 아이콘
좋은 글 감사합니다. rust에 대해서 찾아보고 이제막 hello world를 출력했네요. 헤헤
ace library를 대체할 정도로 괜찮은지 꾸준히 테스트 해봐야겠네욥.
YORDLE ONE
15/07/08 11:12
수정 아이콘
갑자기 러스트에 대해 관심이 생겨서 예전에 자게에서 봤던 글을 찾아 들어오게 됐네요. 글 잘 읽었습니다.

그런데 러스트 말고 다른 질문좀 드려도 될까요? 자바만 계속 하다가 파이썬을 새로 공부하려고 생각중입니다만 파이썬의 심각한 단점이란게 뭔가요?
랜덤여신
15/07/08 13:51
수정 아이콘
앗, 본문에 심각한 단점이라고 써 놓았지만 사람에 따라서는 다르게 생각하는 경우도 있으니 걸러서 들어 주시기 바랍니다. 제가 생각하는 파이선의 단점은 지나치게 '동적' 언어라는 것입니다.

말씀하신 자바가 대표적인 '정적' 언어입니다. 일단 타입이 고정되어 있죠. 어떤 함수가 int 파라미터를 받으면, 그 함수에 String 변수를 넘길 수는 없습니다. 반면에 파이선은 타입이 가변적이라서 어떤 함수가 int를 받을지 String을 받을지 컴파일 시간에 정해지지 않고, 실행 시간에 코드가 어떻게 쓰였는지에 따라 결정됩니다.

이런 동적인 특성은 파이선의 장점이기도 합니다. 프로그래머가 사전에 고민해야 하는 가짓수가 매우 줄어듭니다. 타입 신경 안 쓰고 막 써도 웬만하면 들어맞습니다. 생산성이 높아집니다. 덕분에 잡다한 것은 잊고 알고리즘에 집중할 수 있습니다.

그러나 반대로 프로그래머가 실수를 했을 때 미리 잡아주지 못한다는 단점이 있기도 합니다. 잘못된 타입을 넣어도 문제 없이 동작하니까요.

예전에는 동적 언어로 인한 생산성 향상이 마음에 들어서 파이선을 썼는데, 요즘은 실수를 줄여주는 정적 언어에 관심이 가다 보니 예전에는 좋아 보였던 동적 특성이 지금은 안 좋게 보이는 것이죠. 파이선 자체는 변한 게 없습니다. 하하

혹시 동적 언어를 배워 보신 경험이 전혀 없다면 제가 말한 '심각한 단점'에 구애 받지 마시고 파이선도 한번쯤 배워 보시는 것을 추천합니다. 코딩에 있어서 상당히 자유로워지는 것을 느끼실 수 있을 겁니다. 그게 동적 언어의 장점이자 단점이니까요. 충분히 써 보시고 이러한 특성이 자신에게 맞는지 맞지 않는지 판단해 보는 것이 제일이라고 생각합니다.
YORDLE ONE
15/07/08 15:09
수정 아이콘
아..답변 감사합니다. 내일부터 휴직인데... 휴직하면서 느긋하게 공부해봐야겠네요. 히히
목록 삭게로! 맨위로
번호 제목 이름 날짜 조회 추천
56584 [일반] 청문회 이후 요동치는 충청 민심 [187] 발롱도르14288 15/02/16 14288 1
56582 [일반] 페이스북이 유산상속 기능을 공식적으로 추가했습니다. [26] Leeka9454 15/02/15 9454 2
56581 [일반] [K팝스타] 이진아 대단하네요 [94] 삭제됨11898 15/02/15 11898 3
56580 [일반] 애플 워치, LG 디스플레이 단독 공급 및 여러 이야기 [10] Leeka6092 15/02/15 6092 0
56579 [일반] [스포] 트라이브 보고 왔습니다. [5] 王天君4191 15/02/15 4191 0
56578 [일반] 역사가 끊긴 학문 공동체에 속해있다는 것에 대한 탄식 [75] 삭제됨8090 15/02/15 8090 43
56577 [일반] 한심한 남자의 연애 이야기 -2 [4] 가치파괴자4754 15/02/15 4754 3
56576 [일반] [KBL] "만수 500승"과 잡설 [29] ll Apink ll4337 15/02/15 4337 1
56575 [일반] 사과 누님의 (시나 링고) 음악 들어보기 [36] Dj KOZE8367 15/02/15 8367 4
56574 [일반] 두산의 노경은 선수도 턱을 다쳤다고 합니다. [12] The xian5834 15/02/15 5834 0
56573 [일반] 고마해라...마이 묵읐다 아이가...--;;; [28] Neandertal9321 15/02/15 9321 0
56572 [일반] NIGHTWISH의 신곡이 공개되었습니다. [12] 꼬깔콘3630 15/02/15 3630 1
56571 [일반] 임진왜란에 비해 병자호란은 왜 사극화가 잘 안될까 [77] 발롱도르13749 15/02/15 13749 0
56568 [일반] [MLB] 밤바다를 비추는 눈부신 별 하나에 대한 이야기. [13] 꾹꾹이6837 15/02/15 6837 25
56567 [일반] '남자들은 이해 못 하는 것'(립스틱)에 대한 해설 [60] adgredi24650 15/02/15 24650 37
56565 [일반] 김무성 새누리당 대표, 노무현 전 대통령 묘역 방문 [89] 발롱도르10204 15/02/15 10204 3
56564 [일반] 인생 경기 펼치고 있는 손흥민...!!! [30] ChoA9410 15/02/15 9410 1
56563 [일반] <킹스맨> - 도대체 무슨 생각으로 만든 거지? [56] 마스터충달11612 15/02/15 11612 8
56562 [일반] 똥싸고 휴지로 5번 닦아도 계속 묻어나왔어요 [38] style24507 15/02/14 24507 3
56560 [일반] 이미테이션 게임 보고 왔습니다.(스포 無) + 아카데미 기획전 정보 소개 [11] Hwantastic6026 15/02/14 6026 0
56559 [일반] 세벌식 커뮤니티를 만들었습니다. [12] 소인배5029 15/02/14 5029 4
56558 [일반] 제가 용서받을수 있을까요? [54] 시오냥10786 15/02/14 10786 17
56557 [일반] 러스트 프로그래밍 언어란 무엇이며, 왜 중요한가? [61] 랜덤여신31817 15/02/14 31817 8
목록 이전 다음
댓글

+ : 최근 1시간내에 달린 댓글
+ : 최근 2시간내에 달린 댓글
맨 위로