일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Vue
- 스마트 컨트렉트 함수이름 중복
- 프록시배포구조
- 스마트컨트렉트 예약어 함수이름 중복
- 머신러닝기초
- 스마트컨트렉트 함수이름 중복 호출
- vue기초
- 스마트컨트렉트테스트
- 러스트 기초
- 체인의정석
- SBT표준
- 러스트 기초 학습
- ethers v6
- ethers typescript
- nest.js설명
- ethers
- 티스토리챌린지
- 컨트렉트 배포 자동화
- Vue.js
- git rebase
- ambiguous function description
- ethers websocket
- multicall
- 오블완
- chainlink 설명
- 러스트기초
- ethers type
- 스마트컨트렉트프록시
- 컨트렉트 동일한 함수이름 호출
- rust 기초
- Today
- Total
체인의정석
Rust Ownership , rust ownership을 이해하기 위한 기초(코드로 ownership 살펴보기) 본문
변수와 데이터간의 상호작용
다수의 변수들은 러스트에서 같은 데이터로 다른 방향으로 상호작용한다.
let x = 5;
let y = x;
예시로 위와 같은 값이 지정된다고 가정해 보갰다.
이렇게 되면 x, y 둘다 5를 가지게 되며 스택에 5값이 2개가 쌓이게 된다.
fn main() {
let s1 = String::from("hello");
let s2 = s1;
}
문자열의 경우에 s1이랑 s2가 같이 올라갈 것 같지만 실제로는 그렇지 않다.
실제로는 위에 처럼 string은 포인터, 길이, 용량 3가지 값으로 구성되어 있으며 해당 그룹의 데이터는 스택에 들어가 있다. 실제 콘텐츠는 오른쪽 힙 형태의 메모리에 들어가 있게 된다.
길이는 얼마나 많은 메모리, 바이트, 내용물들이 실제로 사용되고 있는지를 보여주고 용량은 메모리의 총량을 바이트로 보여주게 된다.
s1을 s2에 할당하게 되면 String의 데이터가 복사되는데 이는 포인터, 길이, 용량이 (스택에 있는) 복사된다는 것을 의미한다. 힙에 있는 데이터는 복사되지 않는다.
따라서 위의 그림과 같이 복사가 되게 된다.
만약 복사가 이런식으로 된다면 s2 = s1은 실행자체에 리소스를 엄청 많이 차지할 것이다.
힙 데이터 까지 다 복사가 되기 때문이다.
이전 시간에 우리는 변수가 스코프 밖은 벗어나면 러스트는 자동적으로 함수를 drop하고 힙 메모리를 정리해 준다고 배웠다. 하지만 2번째 위의 그림을 보면 데이터가 같은 위치에 포인터를 연결하고 있다.
이게 문제가 되는데 s2와 s1이 스코프 밖으로 벗어나게 되면 둘다 같은 메모리를 제거하게 된다. 이러한 에러는 double free 에러라고 불린다. 메모리를 2번 free시키면 메모리 corruption을 야기시키며 보안적인 취약점을 유발하게 된다.
위와 같은 상태에서 메모리가 안전하기 위해서 러스트는
fn main() {
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);
}
이런식으로 사용했을 때 s1을 더이상 유효지 않게 만듭니다. 따라서 위의 함수를 실행하면 오류가 나게 된다.
깊은 복사와 얕은 복사를 다른 언어에서 들어봤다면 포인터, 길이, 용량을 복사하는 것은 얕은 복사로 느껴지지만 사실 러스트는 얕은 복사를 사용하는 대신 러스트는 첫번째 변수를 유효하지 않게 만들어 버리는 것입니다. 이걸 우리는 move라고 부른다.
위의 그림처럼 S1이 삭제되었으므로 러스트에서는 이걸 S1이 S2로 Move 되었다고 말한다. 또한 이에 따라서 러스트에서는 자동으로 deepCopy를 해주는 경우가 없기 때문에 런타임에 있어서 퍼포먼스가 더 뛰어나다고 할 수 있다.
요약) String 처럼 변수의 값들이 스택에 저장되는 경우에 한해서 변수가 바뀌게 되면 이전 변수의 선언한 포인터, 길이, 용량을 지워주고 새로운 변수에 할당해주는 것을 MOVE라고 한다.
Clone과 상호작용하는 변수와 데이터
힙 데이터인 String에 대한 깊은 복사를 진행하기 위해서는 우리는 clone이라는 함수를 사용할 수 있다.
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
}
이건 클론에 대한 예시이다. 요건
이걸 그대로 실행시켜주는 것이라고 보면 된다.
Stack만 사용하는 데이터 : Copy
fn main() {
let x = 5;
let y = x;
println!("x = {}, y = {}", x, y);
}
이 예시를 보면 우리는 clone을 하지 않았는데 x, y 값이 둘 다 유지되는 것을 볼 수 있다.
그 이유는 바로 정수형은 사이즈가 알려져 있기 때문에 스택에 집어넣을 수 있는 것이며 이에 따라서 깊은 복사와 얕은 복사가 같다는 것을 의미한다. 이러한 경우 Move가 일어나지 않게 된다.
이러한 경우 Copy 속성을 가지고 있다고 말한다.
만약 Drop이 일어나는 자료형이라면 Copy속성은 가질 수 없게 된다. (힙을 사용하는 과정에서 Move가 일어나게 되고 그 과정에서 Drop이 들어가기 때문이다.)
아래와 같이 데이터의 값들이 정해진 경우라면 Copy 속성을 가질 수 있다.
u 32 같은 정수형
참, 거짓의 boolean 자료형
f64와 같은 부동소수점
그리고 문자형인 char
자료형이 정해지고 고정되어 있는 Tuples (요소들은 모두 Copy 속성을 가지고 있어야 한다.) String이 튜플에 들어가 있다면 안된다.
All the integer types, such as u32.
The Boolean type, bool, with values true and false.
All the floating-point types, such as f64.
The character type, char.
Tuples, if they only contain types that also implement Copy. For example, (i32, i32) implements Copy, but (i32, String) does not.
함수와 오너쉽
fn main() {
let s = String::from("hello"); // s comes into scope
takes_ownership(s); // s's value moves into the function...
// ... and so is no longer valid here
let x = 5; // x comes into scope
makes_copy(x); // x would move into the function,
// but i32 is Copy, so it's okay to still
// use x afterward
} // Here, x goes out of scope, then s. But because s's value was moved, nothing
// special happens.
fn takes_ownership(some_string: String) { // some_string comes into scope
println!("{}", some_string);
} // Here, some_string goes out of scope and `drop` is called. The backing
// memory is freed.
fn makes_copy(some_integer: i32) { // some_integer comes into scope
println!("{}", some_integer);
} // Here, some_integer goes out of scope. Nothing special happens.
함수에서 값을 넘기는 행위는 값을 move 하는 행위와 비슷하다.
위와 같은 예시에서 만약에 takes_ownerships 함수에 들어가게 된다면 s를 함수 밖에서는 사용하지 못하게 된다.
함수에서 s에 대한 소유권을 가져온 후 함수의 스코프에서 나갈 때 drop이 발생하게 되기 때문에 더이상 s에 hello가 할당 되어 있지 않는 것이다.
그러나 5의 경우 처음부터 stack을 사용하기 때문에 소유권이 함수로 이전되었다가 다시 나오더라도 아무 문제가 없게 된다.
스코프와 반환 값
fn main() {
let s1 = gives_ownership(); // gives_ownership moves its return
// value into s1
let s2 = String::from("hello"); // s2 comes into scope
let s3 = takes_and_gives_back(s2); // s2 is moved into
// takes_and_gives_back, which also
// moves its return value into s3
} // Here, s3 goes out of scope and is dropped. s2 was moved, so nothing
// happens. s1 goes out of scope and is dropped.
fn gives_ownership() -> String { // gives_ownership will move its
// return value into the function
// that calls it
let some_string = String::from("yours"); // some_string comes into scope
some_string // some_string is returned and
// moves out to the calling
// function
}
// This function takes a String and returns one
fn takes_and_gives_back(a_string: String) -> String { // a_string comes into
// scope
a_string // a_string is returned and moves out to the calling function
}
값을 반환 하는 것 또한 소유권을 넘길 수 있다.
위의 예시를 보게 되면 gives_ownership에서 some_string을 명시해서 리턴을 해주었다. 그 결과 그 다음 함수인 takes_and_gives_back에서도 소유권을 받아와서 값이 출력된다.
그러나 이런식으로 소유권을 옮기고 drop될때 다시 할당하는 행위를 계속해서 한다면 상당히 복잡해지게 된다.
또한 함수를 실행하고 여러 값들을 리턴 받으려면 아래와 같이 사용할 수 있다.
fn main() {
let s1 = String::from("hello");
let (s2, len) = calculate_length(s1);
println!("The length of '{}' is {}.", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len(); // len() returns the length of a String
(s, length)
}
근데 이렇게 매번 소유권을 다 넘기는 식으로 한다면 변수가 여러개로 리턴된다면 더 복잡해 질 수 있다.
이에 따라서 러스트에서는 reference를 통해 함수에 소유권만 넘기는 작업도 할 수 있다고 한다.
출저 : https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#the-string-type
'블록체인 > Rust' 카테고리의 다른 글
CURG, MOVE 특강 정리 ) (0) | 2023.01.28 |
---|---|
Rust Ownership - Reference and Borrowing (0) | 2023.01.19 |
Rust Ownership , rust ownership을 이해하기 위한 기초 (heap, stack 알아보기) (0) | 2023.01.16 |
Rust 기본 프로그래밍 컨셉 (Control Flow - 조건문, 반복문) (0) | 2023.01.16 |
Rust 기본 프로그래밍 컨셉 (함수) (0) | 2023.01.16 |