유형을 이동 만 가능하고 복사 불가능하게 만들 수 있습니까?
편집자 주 :이 질문은 Rust 1.0 이전에 제기되었으며이 질문의 일부 주장은 Rust 1.0에서 반드시 사실이 아닙니다. 두 버전을 모두 해결하기 위해 일부 답변이 업데이트되었습니다.
이 구조체가 있습니다
struct Triplet {
one: i32,
two: i32,
three: i32,
}
이것을 함수에 전달하면 암시 적으로 복사됩니다. 이제 일부 값은 복사 할 수 없으므로 이동해야한다는 것을 읽었습니다.
이 구조체를 Triplet
복사 불가능하게 만들 수 있습니까? 예를 들어, Triplet
복사 불가능하고 따라서 "이동 가능" 하게 만드는 특성을 구현할 수 있습니까?
나는 Clone
암묵적으로 복사 할 수없는 것을 복사 하기 위해 특성 을 구현 해야하는 어딘가에서 읽었 지만, 그 반대의 방법은 절대로 읽지 않았습니다. 즉, 암묵적으로 복사 할 수 있고 복사 할 수 없게 만드는 것입니다.
그게 말이 되나?
서문 :이 답변은 이전에 기록 된 선택 하에서 내장 특성 -specifically 측면이 구현 -were. 이전 계획 (질문을 받았을 때 적용한 계획)에만 적용되는 섹션을 나타 내기 위해 블록 따옴표를 사용했습니다.Copy
기존 : 기본적인 질문에 답하기 위해
NoCopy
값을 저장하는 마커 필드를 추가 할 수 있습니다 . 예struct Triplet { one: int, two: int, three: int, _marker: NoCopy }
소멸자를 사용하여 (
Drop
trait 구현을 통해) 할 수도 있지만, 소멸자가 아무것도하지 않는 경우 마커 유형을 사용하는 것이 좋습니다.
이제 유형은 기본적으로 이동합니다. 즉, 새 유형을 정의 할 때 유형에 Copy
대해 명시 적으로 구현 하지 않는 한 구현 되지 않습니다 .
struct Triplet {
one: i32,
two: i32,
three: i32
}
impl Copy for Triplet {} // add this for copy, leave it out for move
구현은 모든 유형이 new struct
또는 enum
그 자체 인 경우에만 존재할 수 있습니다 Copy
. 그렇지 않은 경우 컴파일러는 오류 메시지를 인쇄합니다. 또한 유형 에 구현 이없는 경우에만 존재할 수 있습니다 Drop
.
당신이 묻지 않은 질문에 대답하기 위해 ... "무브와 카피는 어떻게 된거 야?":
먼저 두 가지 다른 "사본"을 정의하겠습니다.
- 포인터를 따르지 않고 객체를 바이트 단위로 얕게 복사 하는 바이트 복사본입니다 . 예를 들어를 가지고 있다면
(&usize, u64)
64 비트 컴퓨터에서 16 바이트이고 얕은 복사본은 해당 16 바이트를 가져 와서 복사합니다. 메모리의 다른 16 바이트 청크에서 값 없이 터치usize
의 타단&
. 즉,을 호출하는 것과 같습니다memcpy
. - 의미 사본 값을 복제는 안전하게 이전에 개별적으로 사용할 수있는 새 (다소) 독립적 인 인스턴스를 만들 수 있습니다. 예를 들어의 의미 론적 사본은
Rc<T>
참조 횟수를 늘리는 것과 관련되고,의 의미 론적 사본은Vec<T>
새로운 할당을 생성 한 다음 각 저장된 요소를 이전 항목에서 새 항목 으로 의미 론적으로 복사하는 것을 포함합니다. 이러한 일 수 전체 복사본 (예Vec<T>
) 또는 얕은 (예Rc<T>
닿지 않도록 저장T
),Clone
작업의 최소 량을 의미 타입의 값을 복사 할 필요 느슨하게 정의T
내부&T
로T
.
Rust는 C와 같 으며 값의 모든 값 사용은 바이트 복사입니다.
let x: T = ...;
let y: T = x; // byte copy
fn foo(z: T) -> T {
return z // byte copy
}
foo(y) // byte copy
They are byte copies whether or not T
moves or is "implicitly copyable". (To be clear, they aren't necessarily literally byte-by-byte copies at run-time: the compiler is free to optimise the copies out if code's behaviour is preserved.)
However, there's a fundamental problem with byte copies: you end up with duplicated values in memory, which can be very bad if they have destructors, e.g.
{
let v: Vec<u8> = vec![1, 2, 3];
let w: Vec<u8> = v;
} // destructors run here
If w
was just a plain byte copy of v
then there would be two vectors pointing at the same allocation, both with destructors that free it... causing a double free, which is a problem. NB. This would be perfectly fine, if we did a semantic copy of v
into w
, since then w
would be its own independent Vec<u8>
and destructors wouldn't be trampling on each other.
There's a few possible fixes here:
- Let the programmer handle it, like C. (there's no destructors in C, so it's not as bad... you just get left with memory leaks instead. :P )
- Perform a semantic copy implicitly, so that
w
has its own allocation, like C++ with its copy constructors. - Regard by-value uses as a transfer of ownership, so that
v
can no longer be used and doesn't have its destructor run.
The last is what Rust does: a move is just a by-value use where the source is statically invalidated, so the compiler prevents further use of the now-invalid memory.
let v: Vec<u8> = vec![1, 2, 3];
let w: Vec<u8> = v;
println!("{}", v); // error: use of moved value
Types that have destructors must move when used by-value (aka when byte copied), since they have management/ownership of some resource (e.g. a memory allocation, or a file handle) and its very unlikely that a byte copy will correctly duplicate this ownership.
"Well... what's an implicit copy?"
Think about a primitive type like u8
: a byte copy is simple, just copy the single byte, and a semantic copy is just as simple, copy the single byte. In particular, a byte copy is a semantic copy... Rust even has a built-in trait Copy
that captures which types have identical semantic and byte copies.
Hence, for these Copy
types by-value uses are automatically semantic copies too, and so it's perfectly safe to continue using the source.
let v: u8 = 1;
let w: u8 = v;
println!("{}", v); // perfectly fine
Old: The
NoCopy
marker overrides the compiler's automatic behaviour of assuming that types which can beCopy
(i.e. only containing aggregates of primitives and&
) areCopy
. However this will be changing when opt-in built-in traits is implemented.
As mentioned above, opt-in built-in traits are implemented, so the compiler no longer has automatic behaviour. However, the rule used for the automatic behaviour in the past are the same rules for checking whether it is legal to implement Copy
.
The easiest way is to embed something in your type that is not copyable.
The standard library provides a "marker type" for exactly this use case: NoCopy. For example:
struct Triplet {
one: i32,
two: i32,
three: i32,
nocopy: NoCopy,
}
'code' 카테고리의 다른 글
JSP / Java의 double에서 전체 및 부분 부분을 어떻게 얻습니까? (0) | 2020.09.02 |
---|---|
파이썬에서 한 줄 ftp 서버 (0) | 2020.09.02 |
PHP에서 "=>"는 무엇을 의미합니까? (0) | 2020.09.01 |
HttpClient를 통해 REST API에 빈 본문 게시 (0) | 2020.09.01 |
BCL (기본 클래스 라이브러리) 대 FCL (프레임 워크 클래스 라이브러리) (0) | 2020.09.01 |