Drop
- https://doc.rust-lang.org/std/ops/trait.Drop.html
- Why do we use utility traits?
- to write idiomatic code and design public interfaces for your crates that users will judge to be properly "Rustic".
- it's from std library
- have there broad categories: language extension, marker, public vocabulary traits
- What is DROP?
- Rust drops the value -> a value's owner goes away
- entails freeing everything the value owns: other values, heap storage, and system resources the value owns
- When Rust drops the value?
- a var goes out of scope
- at the end of an expression statement
- trucate a vector
- remove elements from its end
- but dropping values is handled automatically by Rust.
- if you want, you can customize how Rust drops values of your type by implementing the std::ops::Drop trait
- What happens if the value dropped? (When Rust calls drop method)
- How exactly does its memory get cleaned up? (You just showed codes examples about printing logs)
- so the codes : we tried to drop
Vec<String>
- String values are stored in Vector.
Vec
types implements Drop
- dropping its elements
- freeing the heap-allocated buffer they occupied.
String
types which are stored in Vec
- A
String
uses a Vec<u8>
internally to hold its text
- Do not need to implement
Drop
itself.
- so it lets its
Vec
take care of freeing the characters
- same principles extends to values: when one gets dropped
Vec
's implementation of Drop
that actually takes care of freeing each of the strings' contents
- finally freeing the buffer holding the vector's elements
- As for the memory__that holds the Appellation _value itself, it too has some owner, perhaps a local variable_or some data structure, which is responsible for freeing it.
- it means cleanup protocol is up to its owner
- so owner decides when the contents drop (heap-allocated container like vector or box)
- Another case: if a variable's value gets moved elsewhere, so that the variable is uninitialized when the variable goes out of scope. when the value moved away
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1의 값이 s2로 이동됨
// 여기서 s1은 더 이상 유효하지 않음
// println!("{}", s1); // 이 줄은 컴파일 에러를 발생시킴
// s2는 여전히 유효함
println!("{}", s2); // 이는 정상적으로 작동함
}
-
이 상황에서 s1을 프린트하면 에러가 발생함.
- It means Destructor only works when the variable is initialized
- 그렇다면 위 코드에서는 destructor(Drop trait impl)가 s2에게만 작동함(초기화된 상태 + 스코프 밖으로 벗어나서)
- Rust will not try to drop that variable(which is uninitialized bcz it went out of scope)
- Why? because there is no value in it to drop.
- So when there is no value to drop in the variable, then Rust will not drop the variable which has no value
-
생략한 부분 (페이지 305, 러스트 & file descriptor)
-
If a type implements Drop
, then it cannot implement Copy
?
- If a type is
Copy
: that simple byte-for-byte duplication is sufficient to produce an independent copy of the value.
- but it is typically a mistake to call the same
drop
method more than once on the same data
- 같은 값에 대해서 drop을 한번 이상 하는 것은 실수
-
drop
method in the standard prelude
- definition:
fn drop<T>(_x: T) { }
- it receives its args by value, taking ownership from the caller
- then does nothing with it
- Rust drops the value of
_x
when it goes out of scope (just like any other variables)
-
How does it implemented in real Rust codes?
-
Where utility traits at: 깃헙
-
core::ops::Drop
-
About the destructor, Drop::drop
- It consists of two components:
-
- A call for
Drop::drop
for that value
-
- The automatically generated "drop glue" which recursively calls the destructor of all the fields of this value
-
When do we need it to call the destructor?
- As it mentioned in the book, we don't have to implement
Drop
in most cases because Rust automatically calls the destructor of all contained fields.
- Except these special cases which is implementing
Drop
is more useful than calling it automatically
- For types which directly manage a resource (메모리, 파일 디스크립터, 네트워크 소켓 등등)
- 이유: It cleans up the resources once the value of that type is no longer going to be used by freeing the memory or closing the file or socket
HasTwoDrops
의 드랍 트레이트 구현 삭제한 버전 [드랍 순서, 언제 드랍하는지 설명하는 코드)
- When you see the example codes, you can notice
- the type
HasTwoDrops
dropped first, and then HasDrop
dropped next.
- so the owner dropped first and the contents dropped in order
-
Drop order!
[추가로 참고하면 좋을 내용]
- 더 자세한 내용은 문서에 적혀있다. https://doc.rust-lang.org/reference/destructors.html
- If
T: Drop
, calling <T as std::ops::Drop>::drop
- Recursively running the destructor of all of its fields. 데이터 타입별 드랍 오더
- The fields of a struct are dropped in declaration order.
- The fields of the active enum variant are dropped in declaration order.
- The fields of a tuple are dropped in order.
- The elements of an array or owned slice are dropped from the first element to the last.
- The variables that a closure captures by move are dropped in an unspecified order.
- Trait objects run the destructor of the underlying type.
- Other types don't result in any further drops.
- Drop scope 관한 내용도 문서에 있다.
Sized
- What is the sized type? (a.k.a. 컴파일 시간에 사이즈를 알 수 있는 것들)
- the type whose values all have the same size in memory
- Almost all types in Rust are sized. even enums
- enums always occupies enough space to hold its largest variants
Vec<T>
owns a heap-allocated buffer whose size can vary... but the Vec
value itself is a pointer to the buffer, its capacity, and its length, so Vec<T>
is a sized type (예전에 벡터 공부했던 스터디 자료에서 메모리 사진 떠올려보시면 좋을 것 같아요)
- So do we need to implement or do something?
- Nope
- All sized types implement the
std::marker::Sized
trait (no methods or associated types)
- Rust implements Sized traits automatically for all types to which it applies, and you can't implement it yourself.
- Then what is the purpose of existence of
Sized
?
- The only use for
Sized
: a bound for type variables (제약 걸 때만 사용함)
- a bound like
T: Sized
-> requires T
to be a type whose size is known at compile time