Merge branch 'rust-lang-master'

update from origin nomicon
pull/464/head
nomicon-kr 8 months ago
commit 616a66f0f0

@ -4,21 +4,21 @@
## 동량(動量) 타입 (DST)
러스트는 동량(動量) 타입(DST)을 지원합니다: 정적으로 알려진 크기나 정렬선이 없는 타입을 말이죠. 표면적으로는 이것은 좀 말이 되지 않습니다: 러스트는 무언가와 올바르게 작업하기 위해서는 그것의 크기와 정렬선을 *알아야 하거든요!*
이런 면에서 DST는 보통의 타입이 아닙니다. 정적으로 알려진 크기가 없기 때문에, 이런 타입들은 포인터 뒤에서만 존재할 수 있습니다.
러스트는 동량(動量) 타입(DST)을 지원합니다: 정적으로 알려진 크기나 정렬선이 없는 타입을 말이죠. 표면적으로는 이것은 좀 말이 되지 않습니다: 러스트는 무언가와 올바르게 작업하기 위해서는 그것의 크기와 정렬선을 *알아야 하거든요!*
이런 면에서 DST는 보통의 타입이 아닙니다. 정적으로 알려진 크기가 없기 때문에, 이런 타입들은 포인터 뒤에서만 존재할 수 있습니다.
따라서 DST를 가리키는 포인터는 포인터와 DST를 "완성하는" 정보로 이루어진 *넓은* 포인터가 됩니다 (밑에서 더 설명합니다).
언어에서 보이는 주요한 DST는 두 가지가 있습니다:
언어에서 보이는 주요한 DST는 두 가지가 있습니다:
* 트레잇 객체: `dyn MyTrait`
* 슬라이스: [`[T]`][slice], [`str`], 등등
트레잇 객체는 그것이 특정하는 트레잇을 구현하는 어떤 타입을 표현합니다. 정확한 원래 타입은 런타임 리플렉션을 위해 *지워지고,* 타입을 쓰기 위해 필요한 모든 정보를 담고 있는 vtable로 대체됩니다. 트레잇 객체를 완성하는 정보는 이 vtable의 포인터입니다. 포인터가 가리키는 대상의 런타임 크기는 vtable에서 동적으로 요청될 수 있습니다.
트레잇 객체는 그것이 특정하는 트레잇을 구현하는 어떤 타입을 표현합니다. 정확한 원래 타입은 런타임 리플렉션을 위해 *지워지고,* 타입을 쓰기 위해 필요한 모든 정보를 담고 있는 vtable로 대체됩니다. 트레잇 객체를 완성하는 정보는 이 vtable의 포인터입니다. 포인터가 가리키는 대상의 런타임 크기는 vtable에서 동적으로 요청될 수 있습니다.
슬라이스는 어떤 연속적인 저장소에 대한 뷰일 뿐입니다 -- 보통 이 저장소는 배열이거나 `Vec`입니다. 슬라이스 포인터를 완성시키는 정보는 가리키고 있는 원소들의 갯수입니다.
슬라이스는 어떤 연속적인 저장소에 대한 뷰일 뿐입니다 -- 보통 이 저장소는 배열이거나 `Vec`입니다. 슬라이스 포인터를 완성시키는 정보는 가리키고 있는 원소들의 갯수입니다.
가리키는 대상의 런타임 크기는 그냥 한 원소의 정적으로 알려진 크기와 원소들의 갯수를 곱한 것입니다.
구조체는 사실 마지막 필드로써 하나의 동량 타입을 직접 저장할 수 있지만, 그러면 그들 자신도 동량 타입이 됩니다:
구조체는 사실 마지막 필드로써 하나의 동량 타입을 직접 저장할 수 있지만, 그러면 그들 자신도 동량 타입이 됩니다:
```rust
// 직접적으로 스택에 저장할 수 없음
@ -28,7 +28,7 @@ struct MySuperSlice {
}
```
이런 타입은 생성할 방법이 없으면 별로 쓸모가 없지만 말이죠. 현재 유일하게 제대로 지원되는, 커스텀 동량 타입을 만들 방법은 타입을 제네릭으로 만들고 *크기 강제 망각*을 실행하는 것입니다:
이런 타입은 생성할 방법이 없으면 별로 쓸모가 없지만 말이죠. 현재 유일하게 제대로 지원되는, 커스텀 동량 타입을 만들 방법은 타입을 제네릭으로 만들고 *크기 강제 망각*을 실행하는 것입니다:
```rust
struct MySuperSliceable<T: ?Sized> {
@ -53,7 +53,7 @@ fn main() {
## 무량(無量) 타입 (ZST)
러스트는 타입이 공간을 차지하지 않는다고 말하는 것도 허용합니다:
러스트는 타입이 공간을 차지하지 않는다고 말하는 것도 허용합니다:
```rust
struct Nothing; // 필드 없음 = 크기 없음
@ -66,15 +66,15 @@ struct LotsOfNothing {
}
```
무량(無量) 타입(ZST)은, 당연하게도 그 자체로는, 별로 쓸모가 없습니다. 하지만 러스트의 많은 기이한 레이아웃 선택들이 그렇듯이, 그들의 잠재력은 일반적인 환경에서 빛나게 됩니다:
러스트는 무량 타입의 값을 생성하거나 저장하는 모든 작업이 아무 작업도 하지 않는 것과 같을 수 있다는 사실을 매우 이해하거든요. 일단 값을 저장한다는 것부터가 말이 안됩니다 -- 차지하는 공간도 없는걸요.
무량(無量) 타입(ZST)은, 당연하게도 그 자체로는, 별로 쓸모가 없습니다. 하지만 러스트의 많은 기이한 레이아웃 선택들이 그렇듯이, 그들의 잠재력은 일반적인 환경에서 빛나게 됩니다:
러스트는 무량 타입의 값을 생성하거나 저장하는 모든 작업이 아무 작업도 하지 않는 것과 같을 수 있다는 사실을 매우 이해하거든요. 일단 값을 저장한다는 것부터가 말이 안됩니다 -- 차지하는 공간도 없는걸요.
또 그 타입의 값은 오직 하나이므로, 어떤 값이 읽히든 그냥 무에서 값을 만들어내면 됩니다 -- 이것 또한 차지하는 공간이 없기 때문에, 아무것도 하지 않는 것과 같습니다.
이것의 가장 극단적인 예시 중 하나가 `Map``Set`입니다. `Map<Key, Value>`가 주어졌을 때, `Set<Key>``Map<Key, UselessJunk>`를 적당히 감싸는 자료구조로 만드는 것은 흔하게 볼 수 있습니다.
많은 언어들에서 이것은 `UselessJunk` 타입을 위한 공간을 할당하고, `UselessJunk`를 가지고 아무것도 하지 않기 위해서 그 값을 저장하고 읽는 작업을 강제할 겁니다.
이것의 가장 극단적인 예시 중 하나가 `Map``Set`입니다. `Map<Key, Value>`가 주어졌을 때, `Set<Key>``Map<Key, UselessJunk>`를 적당히 감싸는 자료구조로 만드는 것은 흔하게 볼 수 있습니다.
많은 언어들에서 이것은 `UselessJunk` 타입을 위한 공간을 할당하고, `UselessJunk`를 가지고 아무것도 하지 않기 위해서 그 값을 저장하고 읽는 작업을 강제할 겁니다.
이 작업이 불필요하다는 것을 증명하려면 컴파일러는 복잡한 분석을 해야 할 겁니다.
그러나 러스트에서는 우리는 그냥 `Set<Key> = Map<Key, ()>`라고 말할 수 있습니다. 이제 러스트는 컴파일할 때 모든 메모리 읽기와 저장은 의미가 없고, 저장 공간을 할당할 필요도 없다는 것을 알게 됩니다.
그러나 러스트에서는 우리는 그냥 `Set<Key> = Map<Key, ()>`라고 말할 수 있습니다. 이제 러스트는 컴파일할 때 모든 메모리 읽기와 저장은 의미가 없고, 저장 공간을 할당할 필요도 없다는 것을 알게 됩니다.
결과적으로 나오는 코드는 그냥 `HashSet`의 커스텀 구현일 뿐이고, `HashMap`이 값을 처리할 때의 연산은 존재하지 않게 됩니다.
안전한 코드는 무량 타입에 대해서 걱정하지 않아도 되지만, *불안전한* 코드는 크기가 없는 타입의 중요성을 신경써야 합니다. 특히 포인터 오프셋은 아무 작업도 하지 않는 것과 같고, 할당자는 보통 [0이 아닌 크기를 요구합니다][alloc].
@ -86,22 +86,22 @@ struct LotsOfNothing {
## 빈 타입
러스트는 또한 *그 타입의 값을 만들 수조차 없는* 타입을 정의하는 것도 지원합니다. 이런 타입들은 타입 측면에서만 말할 수 있고, 값 측면에서는 절대 말할 수 없습니다.
빈 타입은 형이 없는 열거형을 정의함으로써 만들 수 있습니다:
러스트는 또한 *그 타입의 값을 만들 수조차 없는* 타입을 정의하는 것도 지원합니다. 이런 타입들은 타입 측면에서만 말할 수 있고, 값 측면에서는 절대 말할 수 없습니다.
빈 타입은 형이 없는 열거형을 정의함으로써 만들 수 있습니다:
```rust
enum Void {} // 형 없음 = 비어 있음
```
빈 타입은 무량 타입보다도 더 작습니다. 빈 타입의 예시로 들 만한 것은 타입 측면에서의 접근불가성입니다. 예를 들어, 어떤 API가 일반적으로 `Result`를 반환해야 하지만, 어떤 경우에서는 실패할 수 없다고 합시다.
우리는 이것을 `Result<T, Void>`를 반환함으로써 타입 레벨에서 소통할 수 있습니다.
빈 타입은 무량 타입보다도 더 작습니다. 빈 타입의 예시로 들 만한 것은 타입 측면에서의 접근불가성입니다. 예를 들어, 어떤 API가 일반적으로 `Result`를 반환해야 하지만, 어떤 경우에서는 실패할 수 없다고 합시다.
우리는 이것을 `Result<T, Void>`를 반환함으로써 타입 레벨에서 소통할 수 있습니다.
API의 사용자들은 이런 `Result`의 값이 `Err`가 되기에 *정적으로 불가능하다는 것을* 알고 자신 있게 `unwrap`할 수 있을 겁니다, 왜냐하면 `Err` 값이 있으려면 `Void` 타입의 값이 생산되어야 하거든요.
원칙적으로는 러스트가 이런 사실에 기반하여 몇 가지 흥미로운 분석과 최적화를 수행할 수 있을 겁니다. 예를 들어 `Result<T, Void>``Err` 형이 실제로 존재하지 않기에, 그냥 `T`로 표현할 수 있겠죠
원칙적으로는 러스트가 이런 사실에 기반하여 몇 가지 흥미로운 분석과 최적화를 수행할 수 있을 겁니다. 예를 들어 `Result<T, Void>``Err` 형이 실제로 존재하지 않기에, 그냥 `T`로 표현할 수 있겠죠
(엄격하게 말하면, 이것은 보장되지 않은 최적화일 뿐이고, `T``Result<T, Void>` 중 하나를 다른 하나로 변질시키는 것은 아직 미정의 동작입니다).
다음의 코드도 컴파일 *될 수도 있을* 겁니다:
다음의 코드도 컴파일 됩니다:
```rust,compile_fail
```rust
enum Void {}
let res: Result<u32, Void> = Ok(0);
@ -110,11 +110,9 @@ let res: Result<u32, Void> = Ok(0);
let Ok(num) = res;
```
하지만 아직 이 꼼수는 통하지 않습니다.
빈 타입에 대한 마지막 하나의 조그만 사실은, 빈 타입을 가리키는 생 포인터는 놀랍게도 유효하게 생성할 수 있지만, 그것을 역참조하는 것은 말이 안되기 때문에 미정의 동작이라는 것입니다.
우리는 C의 `void*` 타입을 `*const Void`로 설계하는 것을 추천하지 않습니다. 많은 사람들이 이렇게 했지만 얼마 지나지 않아 문제에 부딪혔는데, 러스트는 불안전한 코드로 빈 타입의 값을 만드려고 하는 것을 막는 안전 장치가 없고,
우리는 C의 `void*` 타입을 `*const Void`로 설계하는 것을 추천하지 않습니다. 많은 사람들이 이렇게 했지만 얼마 지나지 않아 문제에 부딪혔는데, 러스트는 불안전한 코드로 빈 타입의 값을 만드려고 하는 것을 막는 안전 장치가 없고,
만약 빈 타입의 값을 만들면, 그것은 미정의 동작이기 때문입니다. 이것은 특별히 문제가 되었는데, 개발자들이 생 포인터를 레퍼런스로 바꾸는 습관이 있었고 `&Void` 값을 만드는 것 *역시* 미정의 동작이기 때문입니다.
`*const ()` (혹은 비슷한 타입)은 `void*`에 대응해서 무리 없이 잘 동작하고, 안전성의 문제 없이 레퍼런스로 만들 수 있습니다.
@ -122,7 +120,7 @@ let Ok(num) = res;
## 외래 타입
*외래 타입*으로 불리는, 알 수 없는 크기의 타입을 추가하여 러스트 개발자들이 C의 `void*`나 다른 "선언되었지만 정의되지 않은" 타입들을 좀더 정확하게 설계하자는, [승인된 RFC가][extern-types] 있습니다.
*외래 타입*으로 불리는, 알 수 없는 크기의 타입을 추가하여 러스트 개발자들이 C의 `void*`나 다른 "선언되었지만 정의되지 않은" 타입들을 좀더 정확하게 설계하자는, [승인된 RFC가][extern-types] 있습니다.
하지만 러스트 2018 기준으로, [이 기능은 `size_of_val::<MyExternType>()`이 어떻게 작동해야 하는지에 걸려서 대기 상태에 갇혀 있습니다][extern-types-issue].
[extern-types]: https://github.com/rust-lang/rfcs/blob/master/text/1861-extern-types.md

@ -1,15 +1,15 @@
# 다른 데이터 표현들
러스트는 기본으로부터 다른 데이터 설계 전략을 구성하게 해 줍니다.
러스트는 기본으로부터 다른 데이터 설계 전략을 구성하게 해 줍니다.
[불안전 코드 가이드라인][unsafe_guide]도 있습니다 (**비표준이니** 주의하세요).
## repr(C)
이것은 가장 중요한 `repr`입니다. 이것은 매우 간단한 의도를 가지고 있습니다: C가 하는대로 하라는 것이죠. 필드들의 정렬 순서, 크기, 정렬선은 C나 C++에서 되는 것 같이 될 겁니다.
어떤 타입이든 FFI 경계를 넘겨 보내려면 `repr(C)`로 표현되어야 하는데, 이는 C가 프로그래밍 세계의 공용어이기 때문입니다. 이것은 값을 다른 타입으로 재해석하는 것과 같은, 데이터 레이아웃을 가지고 정교한 장난을 수월하게 칠 수 있기 위해서 필수적입니다.
이것은 가장 중요한 `repr`입니다. 이것은 매우 간단한 의도를 가지고 있습니다: C가 하는대로 하라는 것이죠. 필드들의 정렬 순서, 크기, 정렬선은 C나 C++에서 되는 것 같이 될 겁니다.
어떤 타입이든 FFI 경계를 넘겨 보내려면 `repr(C)`로 표현되어야 하는데, 이는 C가 프로그래밍 세계의 공용어이기 때문입니다. 이것은 값을 다른 타입으로 재해석하는 것과 같은, 데이터 레이아웃을 가지고 정교한 장난을 수월하게 칠 수 있기 위해서 필수적입니다.
우리는 [rust-bindgen]과 [cbindgen]를 둘 다, 혹은 둘 중 하나를 써서 당신 대신 FFI 경계를 관리하기를 매우 권장합니다. 러스트 팀은 이 프로젝트들과 긴밀하게 작업하여 이들이 튼튼하게 작동하고,
우리는 [rust-bindgen]과 [cbindgen]를 둘 다, 혹은 둘 중 하나를 써서 당신 대신 FFI 경계를 관리하기를 매우 권장합니다. 러스트 팀은 이 프로젝트들과 긴밀하게 작업하여 이들이 튼튼하게 작동하고,
타입 레이아웃과 `repr`들에 대한 현재와 미래의 보장에 잘 맞도록 신경쓰고 있습니다.
`repr(C)`와 러스트의 (C보다) 이상한 데이터 설계 기능의 상호작용은 주의해야 합니다. "FFI를 위한" 것과 "데이터 표현을 바꾸기" 위한 두 가지 목적이 동시에 있기 때문에, `repr(C)`는 FFI 경계로 보내면 말이 안되거나 문제가 생길 수 있는 타입들에 적용할 수 있습니다.
@ -30,7 +30,7 @@
## repr(transparent)
`#[repr(transparent)]`은 크기가 0이 아닌 하나의 필드를 가지고 있는 (무량 필드는 더 있어도 됩니다) 구조체나 형이 하나인 열거형에만 쓰일 수 있습니다.
`#[repr(transparent)]`은 크기가 0이 아닌 하나의 필드를 가지고 있는 (무량 필드는 더 있어도 됩니다) 구조체나 형이 하나인 열거형에만 쓰일 수 있습니다.
이것의 효과는 구조체/열거형의 전체 레이아웃과 ABI가 그 하나의 필드와 완전히 동일하도록 보장된다는 것입니다.
> 주의: `repr(transparent)`를 공용체에 적용하는 `transparent_unions` nightly 기능이 있지만,
@ -46,13 +46,13 @@
## repr(u*), repr(i*)
이것들은 필드 없는 열거형을 만들기 위한 크기를 지정합니다. 식별자가 이 크기를 벗어나서 오버플로우되면 컴파일할 때 에러가 발생할 겁니다. 하지만 이것을 러스트에서 허용할 수도 있습니다: 오버플로우된 순간 0이 되게 명시적으로 말하는 것이죠.
그러나 러스트는 열거형의 두 개의 형이 같은 식별자를 가지는 것을 허용하지 않을 겁니다.
이것들은 필드 없는 열거형을 만들기 위한 크기와 부호를 지정합니다. 식별자가 이 정수를 벗어나서 오버플로우되면 컴파일할 때 에러가 발생할 겁니다. 하지만 이것을 러스트에서 허용할 수도 있습니다: 오버플로우된 순간 0이 되게 명시적으로 말하는 것이죠.
그러나 러스트는 열거형의 두 개의 형이 같은 식별자를 가지는 것을 허용하지 않을 겁니다.
"필드 없는 열거형"이라는 용어는 단지 열거형이 그 형에서 데이터를 가지지 않는다는 것을 말합니다. `repr(u*)``repr(C)`가 없는 필드 없는 열거형은 여전히 러스트 타입이고, 안정적인 ABI 표현이 존재하지 않습니다.
`repr`을 더하는 것은 ABI를 위해 이 열거형이 지정된 크기의 정수로 다뤄지게 합니다.
"필드 없는 열거형"이라는 용어는 단지 열거형이 그 형에서 데이터를 가지지 않는다는 것을 말합니다. `repr(u*)``repr(C)`가 없는 필드 없는 열거형은 여전히 러스트 타입이고, 안정적인 ABI 표현이 존재하지 않습니다.
`repr`을 더하는 것은 ABI를 위해 이 열거형이 지정된 타입의 정수로 다뤄지게 합니다.
만약 열거형이 필드가 있다면, 타입의 정해진 레이아웃이 있다는 점에서 효과는 `repr(C)`의 효과와 비슷하게 됩니다. 이것은 열거형을 C 코드에 넘기고, 그 타입의 실제 표현을 접근하고 직접 그 태그와 필드를 조작할 수 있게 해 줍니다.
만약 열거형이 필드가 있다면, 타입의 정해진 레이아웃이 있다는 점에서 효과는 `repr(C)`의 효과와 비슷하게 됩니다. 이것은 열거형을 C 코드에 넘기고, 그 타입의 실제 표현을 접근하고 직접 그 태그와 필드를 조작할 수 있게 해 줍니다.
자세한 내용은 [RFC][really-tagged]를 참고하세요.
`repr`은 구조체에는 아무 효과도 없습니다.
@ -81,8 +81,8 @@ assert_eq!(16, size_of::<MyReprOption<&u16>>());
`repr(packed)`는 타입에 어떤 여백도 제거하고, 한 바이트에 정렬하도록 러스트에 강제합니다. 이것은 메모리 사용량은 줄여주지만, 아마 다른 부작용들을 초래할 것입니다.
더 자세히 말하자면, 대부분의 아키텍쳐는 값들이 제대로 정렬되는 것을 *매우* 선호합니다. 이것은 제대로 정렬되지 않은 읽기는 뒤로 미뤄지거나 (x86), 심지어는 강제종료됩니다 (일부의 ARM 칩들).
이렇게 "압축"된 필드를 직접 읽거나 저장하는 간단한 경우들에서는, 컴파일러가 쉬프트나 마스크 같은 것들로 정렬 문제를 간단히 수정할 수 있을지도 모릅니다.
더 자세히 말하자면, 대부분의 아키텍쳐는 값들이 제대로 정렬되는 것을 *매우* 선호합니다. 이것은 제대로 정렬되지 않은 읽기는 뒤로 미뤄지거나 (x86), 심지어는 강제종료됩니다 (일부의 ARM 칩들).
이렇게 "압축"된 필드를 직접 읽거나 저장하는 간단한 경우들에서는, 컴파일러가 쉬프트나 마스크 같은 것들로 정렬 문제를 간단히 수정할 수 있을지도 모릅니다.
그러나 그 압축된 필드에 레퍼런스를 단다면, 컴파일러가 정렬되지 않은 읽기를 피할 수 있는 코드를 낼 수 있을 것 같지는 않습니다.

@ -5,13 +5,13 @@
* 생 포인터 역참조하기
* `unsafe` 함수 호출하기 (C 함수나, 컴파일러 내부, 그리고 할당자를 직접 호출하는 것 포함)
* `unsafe` 트레잇 구현하기
* `static` 변수의 값을 변경하기
* 가변 정적 변수를 접근하거나 수정하기
* `union` 의 필드를 접근하기
이게 전부입니다. 이런 연산들이 불안전의 영역으로 추방된 이유는, 이것들 중 하나라도 잘못 사용할 경우 그토록 두렵던 미정의 동작을 일으키기 때문입니다.
이게 전부입니다. 이런 연산들이 불안전의 영역으로 추방된 이유는, 이것들 중 하나라도 잘못 사용할 경우 그토록 두렵던 미정의 동작을 일으키기 때문입니다.
미정의 동작을 일으키면 컴파일러가 당신의 프로그램에 임의의 나쁜 짓들을 할 수 있는 모든 권리를 얻게 됩니다. 당연하게도 미정의 동작은 *일으켜서는 안됩니다.*
C와 다르게, 미정의 동작은 러스트에서는 꽤 제한되어 있습니다. 러스트의 코어 언어가 막으려고 하는 것들은 이런 것들입니다:
C와 다르게, 미정의 동작은 러스트에서는 꽤 제한되어 있습니다. 러스트의 코어 언어가 막으려고 하는 것들은 이런 것들입니다:
* 달랑거리거나 정렬되어 있지 않은 포인터를 역참조하는 것 (`*` 연산자를 사용해서) (밑 참조)
* [레퍼런스 규칙][alias] 을 어기는 것
@ -35,17 +35,17 @@ C와 다르게, 미정의 동작은 러스트에서는 꽤 제한되어 있습
값을 "생산하는" 일은 값이 할당되거나, 함수/기본 연산에 전달되거나, 함수/기본 연산에서 반환될 때 일어납니다.
레퍼런스/포인터가 "달랑거린다"는 것은 그것이 널이거나 그것이 가리키는 바이트가 모두 같은 할당처에 있는 것이 아니라는 뜻입니다 (그 바이트들은 모두 *어떤* 할당처에는 있어야 합니다).
그것이 가리키는 바이트들의 너비는 포인터 값과 참조되는 타입의 크기에 따라 결정됩니다. 따라서 만약 너비가 비어 있다면, "달랑거리는" 것은 "널"인 것과 같습니다. 슬라이스와 문자열은 그들의 전체 범위를 가리킨다는 것을 유의한다면,
레퍼런스/포인터가 "달랑거린다"는 것은 그것이 널이거나 그것이 가리키는 바이트가 모두 같은 할당처에 있는 것이 아니라는 뜻입니다 (그 바이트들은 모두 *어떤* 할당처에는 있어야 합니다).
그것이 가리키는 바이트들의 너비는 포인터 값과 참조되는 타입의 크기에 따라 결정됩니다. 따라서 만약 너비가 비어 있다면, "달랑거리는" 것은 "널"인 것과 같습니다. 슬라이스와 문자열은 그들의 전체 범위를 가리킨다는 것을 유의한다면,
길이 메타데이터가 너무 크지 않도록 하는 것이 중요해집니다 (특히, 할당량과 그에 따른 슬라이스와 문자열은 `isize::MAX` 바이트보다 클 수 없습니다). 만약 어떤 이유로 이것이 거추장스럽다면, 생 포인터를 쓰는 것을 고려해 보세요.
그게 전부입니다. 그것이 러스트에 있는 미정의 동작의 모든 원인입니다. 물론 불안전한 함수들과 트레잇들은 프로그램이 지켜야 하는 임의의 다른 제약들을 걸 수 있고, 그것을 어기면 미정의 동작이 일어나겠죠.
그게 전부입니다. 그것이 러스트에 있는 미정의 동작의 모든 원인입니다. 물론 불안전한 함수들과 트레잇들은 프로그램이 지켜야 하는 임의의 다른 제약들을 걸 수 있고, 그것을 어기면 미정의 동작이 일어나겠죠.
예를 들어, 할당자 API는 할당되지 않은 메모리를 해제하는 것은 미정의 동작이라고 정의합니다.
그러나 이런 제약들을 어기면 결국 위의 문제들 중 하나로 이어지게 될 것입니다. 어떤 추가적인 제약들은 컴파일러 내부가 코드를 최적화하는 과정에서 하는 특별한 가정들에서부터 비롯될 수도 있습니다.
예를 들어, `Vec``Box` 는 그들의 포인터가 항상 널이 아니도록 하는 내부 코드를 사용합니다.
그러나 이런 제약들을 어기면 결국 위의 문제들 중 하나로 이어지게 될 것입니다. 어떤 추가적인 제약들은 컴파일러 내부가 코드를 최적화하는 과정에서 하는 특별한 가정들에서부터 비롯될 수도 있습니다.
예를 들어, `Vec``Box` 는 그들의 포인터가 항상 널이 아니도록 하는 내부 코드를 사용합니다.
러스트는 이 외의 다른 애매한 작업들에는 꽤나 관대합니다. 러스트는 이런 작업들을 "안전하다"고 판단합니다:
러스트는 이 외의 다른 애매한 작업들에는 꽤나 관대합니다. 러스트는 이런 작업들을 "안전하다"고 판단합니다:
* 데드락 (교착 상태)
* [경합 조건][race] 이 있는 것

Loading…
Cancel
Save