C++ 및 Rust의 전송 값

19445 단어 rustcppbeginners
C++와 Rust는 항상 서로 비교한다.그것들은 기능과 유연성 면에서 비슷한 공간을 차지한다. 쓰레기 수집기가 없기 때문에 자원이 제한된 분야에 적응할 수 있지만, 모두 C와 같은 언어보다 풍부한 고급 도구를 제공하여 안전성과 정확성을 높인다.
그러나 언어마다 프로그램을 작성하는 체험은 크게 다를 수 있다.이러한 차이가 발생하면 Rust의 초보자들은 값을 통해 파라미터를 전달할 때 어떤 일이 일어날지 곧 알게 될 것이다.Rust가 이런 상황을 처리하는 방식은 C++와 다르기 때문에 원인을 탐구할 필요가 있다.

C++
C++에서 값 전달을 통해 대상의 사본을 함수에 전달합니다.이것은 정수와 같은 기본체에 아주 좋다. 나의 5와 너의 5는 같다.그것들은 메모리에서 서로 다른 값이다. 이 사실은 그것들의 사용에 아무런 영향을 주지 않는다. 왜냐하면 5의 의미는 상하문이나 상태에 의존하지 않기 때문이다.하지만 이런 일도 많다.C++에서 객체를 복사할 때 복제 구조 함수가 호출됩니다.이러한 원형은 다음과 같습니다.
classname (const classname &obj);
대상이 매개 변수로 방법에 전달될 때, 이 구조 함수는 대상을 함수체에 복제하는 데 사용된다.매개변수 목록의 시작 키워드 "const"를 봅니다.이것은 우리가 이 구조 함수를 사용하여 초기 대상을 변경할 수 없다는 것을 의미한다.반대로, 그것은 단지 새로운 복사본을 만들 뿐, 이것은 어떠한 함수에서도 사용된다.다음은 데이터 구성원 하나, 기본 구조 함수 하나, Getter 하나, setter만 있는 간단한 클래스입니다.
class CoolObject
{
    int coolValue;

public:
    CoolObject()
    {
        coolValue = 5;
    }
    int getCoolValue() const
    {
        return coolValue;
    }
    void setCoolValue(int val)
    {
        coolValue = val;
    }
};
객체 중 하나를 값별로 가져오는 함수를 작성하여 10으로 설정합니다.
#include <iostream>

void setCoolValueToTen(CoolObject co)
{
    using std::cout;
    cout << "Current: " << co.getCoolValue() << " | Setting...\n";
    co.setCoolValue(10);
    cout << "New: " << co.getCoolValue() << "\n";
};
만약 우리가 두 개를 만들고 그 중 하나에 이 함수를 사용한다면, 너는 그것이 변하지 않을 것이라고 생각할 것이다. 그렇지?
int main()
{
    using std::cout;
    CoolObject co1;
    CoolObject co2;
    cout << "co1: " << co1.getCoolValue() << " | co2: " << co2.getCoolValue() << "\n";
    setCoolValueToTen(co2);
    cout << "co1: " << co1.getCoolValue() << " | co2: " << co2.getCoolValue();
    return 0;
}
반대로 우리는 다음과 같은 결과를 얻었다.
co1: 5 | co2: 5
Current: 5 | Setting...
New: 10
co1: 5 | co2: 5
setCoolValueToTen() 함수의 코드는 자신의 복사본에서 실행되며,co2에서 생성되며, 전송될 때co2와 같지만,co2와는 완전히 다르다.이 로컬 실례에서 setter를 호출하는 것은 이산화탄소에 영향을 주지 않는다. 왜냐하면 그것은 더 이상 관련되지 않기 때문이다.
값에 따라 전달되면 모든 변경 사항이 이 새 로컬 복사본에 끼어 예상한 목표로 되돌아오지 않습니다.원문을 참고하면 이 문제를 해결할 수 있다.
void reallySetCoolValueToTen(CoolObject &co) // Just take a reference - rest is identical!
{
    using std::cout;
    cout << "Current: " << co.getCoolValue() << " | Setting...\n";
    co.setCoolValue(10);
    cout << "New: " << co.getCoolValue() << "\n";
}

int main()
{
    using std::cout;
    CoolObject co1;
    CoolObject co2;
    cout << "co1: " << co1.getCoolValue() << " | co2: " << co2.getCoolValue() << "\n";
    setCoolValueToTen(co2);
    cout << "co1: " << co1.getCoolValue() << " | co2: " << co2.getCoolValue() << "\n";
    reallySetCoolValueToTen(co2);
    cout << "co1: " << co1.getCoolValue() << " | co2: " << co2.getCoolValue() << "\n";
    return 0;
}
두 번째 호출은 예상대로 작동합니다.
co1: 5 | co2: 5
Current: 5 | Setting...
New: 10
co1: 5 | co2: 5
Current: 5 | Setting...
New: 10
co1: 5 | co2: 10

녹슬다
Rust에서 이 애플릿을 다시 실현해 봅시다.다음은 DellCoolObject입니다.
struct CoolObject {
    cool_value: i32,
}

impl CoolObject {
    fn get_cool_value(&self) -> i32 {
        self.cool_value
    }
    fn set_cool_value(&mut self, val: i32) {
        self.cool_value = val;
    }
}

impl Default for CoolObject {
    fn default() -> Self {
        Self { cool_value: 5 }
    }
}
값을 10으로 설정하고 값을 기준으로 매개변수를 지정하는 함수가 필요합니다.
fn set_cool_value_to_ten(mut co: CoolObject) {
    println!("Current: {} | Setting...", co.get_cool_value());
    co.set_cool_value(10);
    println!("New: {}", co.get_cool_value());
}
우리는 이미 하나의 문제를 보기 시작했다. 우리는 C++에서처럼 사전에 물어보지 않고 값을 바꿀 수 없다.만약 내가 mut를 매개 변수 목록에 포함하지 않았다면, set_cool_value() 호출은 "co를 가변적으로 대여할 수 없다. 왜냐하면 그것은 가변적으로 성명되지 않았기 때문이다."라고 불평할 것이다.우리는 컴파일러에게 우리가 대상에 대해 변이를 진행할 계획이라고 명확하게 알려야 한다.
C++ 버전을 에뮬레이션하기 위한 첫 번째 단계를 살펴보겠습니다.
fn main() {
    let co1 = CoolObject::default();
    let co2 = CoolObject::default();
    println!("co1: {} | co2: {}", co1.get_cool_value(), co2.get_cool_value());
    set_cool_value_to_ten(co2);
    println!("co1: {} | co2: {}", co1.get_cool_value(), co2.get_cool_value());
}
이 코드를 컴파일하려고 하면 다음과 같은 오류가 발생합니다.
error[E0382]: borrow of moved value: `co2`
  --> src/main.rs:34:57
   |
31 |     let co2 = CoolObject::new();
   |         --- move occurs because `co2` has type `CoolObject`, which does not implement the `Copy` trait
32 |     println!("co1: {} | co2: {}", co1.get_cool_value(), co2.get_cool_value());
33 |     set_cool_value_to_ten(co2);
   |                           --- value moved here
34 |     println!("co1: {} | co2: {}", co1.get_cool_value(), co2.get_cool_value());
   |                                                         ^^^ value borrowed here after move

error: aborting due to previous error
그게 문제야.C++에서 값을 전달할 때, 컴파일러는 자신이 무엇을 하는지 알고 있다고 가정하고, 실제적으로 의미가 없어도 복제 구조 함수를 호출합니다.만약 당신이 복제 구조 함수를 수동으로 정의하지 않았다면, 걱정하지 마십시오. 컴파일러가 당신을 위해 그것을 생성하고 호출할 것입니다.어쨌든, 너는 이미 값을 전달했으니, 이것은 틀림없이 네가 원하는 것이다!
녹이 슬어 브레이크가 고장나다.값을 전달할 때, 실제 값의 소유권을 이동합니다.그것은 원시 대상을 복제하는 것이 아니라 외부에서 대상을 도입하는 것이다. 그러나 주의해야 할 것은 호출 범위는 더 이상 이 값을 가지지 않고, 새 함수는 이 값을 가지고 있다.set_cool_value_to_ten() 주체의 끝에 도달했을 때 이 값은 범위를 초과합니다!얘 떨어졌어.우리가 다음 줄에서 다시 인용하려고 시도할 때 co2, 우리는 할 수 없다. 그것은 더 이상 우리의 것이 아니다.
Rust의 모든 가치는 소유자 하나뿐입니다.너는 임의의 여러 개의 변할 수 없는 인용을 빌릴 수 있다. 우리가 get_cool_value(&self)를 호출했을 때 이렇게 하거나, 예를 들어 really_set_cool_value_to_ten(co: &mut CoolObject)를 빌릴 수 있지만, 만약 빌리지 않았다면, 예를 들어 set_cool_value_to_ten(mut co: CoolObject) 이 값의 소유권이 바뀔 것이라는 것을 알 수 있다.
이것은 C++에서 흔히 볼 수 있는 전달 값 오류를 피합니다. 즉, 대상을 처리하고 있다고 생각하지만, 실제로는 복사본만 처리하고 있습니다.C++는 묵묵히 일을 시작하려고 시도할 뿐이며, 당신과 같은 페이지에 없을 수도 있습니다.녹이 슬는 것은 매우 뚜렷하다.대상이 Copy 특징을 실현하면, 이 값을 복제하려고 시도할 수도 있다는 것을 명확하게 알려준다. 물론, 이것은 여전히 이 문제를 해결할 수 없다.C++와 마찬가지로 해결 방안은 원본 값을 인용하는 것이지 이동 값을 인용하는 것이 아니다.C++에서는 "인용"이라고 말하지만, Rust에서는 "가변 대여"라고 부른다.
fn really_set_cool_value_to_ten(co: &mut CoolObject) {
    println!("Current: {} | Setting...", co.get_cool_value());
    co.set_cool_value(10);
    println!("New: {}", co.get_cool_value());
}
우리는 또한 co2 자체가 변할 수 있다는 것을 성명해야 한다.
fn main() {
    let co1 = CoolObject::default();
    let mut co2 = CoolObject::default(); // right here
    println!("co1: {} | co2: {}", co1.get_cool_value(), co2.get_cool_value());
    really_set_cool_value_to_ten(&mut co2); // and pass a mutable reference
    println!("co1: {} | co2: {}", co1.get_cool_value(), co2.get_cool_value());
}
이것은 내가 C++ 대신 Rust를 사용하는 이유 중 하나를 설명한다.C++에서, 프로그래머는 언어가 어떻게 작동하는지 모든 세부 사항만 알 수 있고, 컴파일러가 사용하는 스텔스 동작은 주저하지 않는다.코드를 읽어서 어디서 이 오류를 범했는지 찾아낼 수 없다. 설령 이 문제를 완전히 깨닫는다 하더라도 100% 상황에서 그것을 피하기에는 부족하다.다른 한편, 녹이 슬면 어리석은 것을 요구하지 않는다.이런 상황에서 컴파일러는 간단한 영어로 왜 나의 코드가 정확하지 않은지, 그리고 그것을 어떻게 복구하는지 나에게 알려줄 수 있다.
Natalia Y 의 Unsplash 사진

좋은 웹페이지 즐겨찾기