알아!?Rust의 소유권 시스템

30660 단어 Rusttech
때로는 "Rust의 소유권은 어렵다"고 말하기도 하지만, 소유권 시스템을 이해하는 데 도움을 주는 기사로 쓰고 싶다.

소유권은 자원을 개방하는 권리를 가리킨다


소유권은 함수로 자원을 개방할 권리다.가졌다기보다는 소비된 이미지에 가깝다.
다음은 변수의 속박에 대한 해설이 많은데 아마도 걸려 넘어진 것은 함수일 것이다.
let a = String::from("abc");
let b = a;
println!("{}", a); // bに所有権が移動するのでコンパイルエラー

let c: u32 = 1;
let d: u32 = c;
println!("c = {}, d = {}", c, d); // 基本データ型はCopyトレイトを実装しているのでコンパイル可能
, 지금부터 함수의 예로 설명하자면...
다음 함수foob: Bar에 속한다.함수가 끝났을 때b가 이미 가지고 있기 때문에 버려집니다(Drop).함수가 끝났을 때b가 없으면 버려지지 않습니다(나중에 자세히 설명합니다).
fn foo(b: Bar) {
  /* 直接的にbを所有して利用 */
} // この関数がbの所有権を持っているのでDropされる

let a = Bar::new();
foo(a);
// a は fooに所有されてしまうので、使えなくなる
이 자원의 소유권은 여러 곳에서 소유할 수 없다.소유자가 마지막으로 그 자원을 개방했기 때문에 항상 한 곳이다.이것을 위반하는 코드를 썼으면 컴파일할 수 없습니다.

소유권의 이동


소유권을 빼앗은 후 다른 함수에 소유권을 양도하면 어떻게 될까.다음 예에서 소유권이 self.bar_list.pushfoo로 이전되었기 때문에 폐기되지 않는다.이런 소유권 이동을 이동이라고 부른다.참고로 실체형을 정의했지만 복사 비용도 필요 없다.이동 중에는 복제가 발생하지 않습니다.
fn foo(&mut self, b: Bar) {
  self.bar_list.push(b); // ムーブ
} // この関数がbの所有権を持っていないのでDropしない
폐기된 시간은 변하지만 소유권은 박탈되지 않는다.
let a = Bar::new();
x.foo(a); // ムーブ
// a は fooに所有(消費)されてしまうので、使えなくなる
어디서 폐기됐는지 일일이 설명하지만 실제 코드를 쓸 때는 개의치 않는다.그건 b에 맡겨야 하니까.
참고로 파동이 발생하는 조작은 다음과 같다(실천 Rust 입문 참조).
  • 모델 매칭rustc 표현식 뿐만 아니라 match 문장 변수의 제약도 포함)
  • 함수 호출
  • 함수나 블록의 반환
  • 구조기
  • let 클론
  • https://amzn.to/2WYAimY

    소유권의 대여


    어떤 자원의 소유권 함수를 호출한 후, 이 자원을 이용하려면 복사본을 제출하는 것 외에, 이 함수의 반환값으로만 자원의 소유권을 되돌려받을 수 있습니다.불편하다
    fn foo(b: Bar) -> Bar {
      // ... 
      b // 関数からのリターンで所有権をムーブできる
    }
    
    let a = Bar::new();
    let a = foo(a.clone()); // 複製をムーブする
    println!("{:?}", a);
    
    이럴 때 사용하는 것은 참고용 차용이다.매개변수 유형은 참조입니다.빌려썼으면 그 이름대로 폐기되지 않았을 텐데.그 함수는 자원이 없기 때문이다.
    fn foo(b: &Bar) {
      /* bを借用して利用 */
    } // 参照のbはDropされない
    
    let a = Bar::new();
    foo(&a); // 借用
    println!("{:?}", a);
    
    은 대여(참조)를 사용하는 일반적인 예로 멤버의 참조를 반환하는 경우입니다.
    pub fn name(&self) -> &str {
      &self.name
    }
    

    이동(이동)/대여(폴로)의 불량 예


    다음 공식 문서는 참조가 될 수 있습니다.보충하면서 생각하다.
    https://sinkuu.github.io/api-guidelines/flexibility.html#호출자가 데이터를 복제할 시기를 결정합니다 - c-caller-control

    인용을 받아들이지만 함수 내부에서 특별히 복제한 예


    파라미터 소유권이 필요한 함수는 차용 복제가 아니라 소유권을 받아들여야 한다.
    // 良い例:
    fn foo(b: Bar) {
      /* 直接的にbを所有して利用 */
    } // この関数がbの所有権を持っているのでDropされる
    
    // 悪い例:
    fn foo(b: &Bar) {
      let b = b.clone(); // わざわざcloneする…。
      /* 複製後にbを所有して利用 */
    } // 複製されたbがDropされるが、参照のbは借用なのでDropされない
    
    나쁜 예는 상당히 자유롭지만 무의미한move을 하기보다는 처음부터 소유권을 박탈하는 것이 좋다.흔들리면 곤란해진다면 다음과 같다clone.나쁜 예와 무엇이 다르다면 호출자는 복제의 결정권을 가지고 디자인에 유연성을 가진다.
    let a = Bar::new();
    foo(a.clone()); // 呼び出し元が複製を作るかを決める
    println!("{:?}", a);
    

    소유권을 빼앗지만 함수 내부에서 소유권을 이용하지 않는 예


    함수에 매개 변수의 소유권이 필요하지 않으면, 최종drop이 아닌 가변 또는 비가변 차용을 받아야 한다.
    // 良い例:
    fn foo(b: &Bar) {
      /* bを借用して利用 */
    } // 参照のbはDropされない
    
    // 悪い例:
    fn foo(b: Bar) {
      /* 実質的にbは所有されず借用のみ。関数の最後にDropされる */
    } // この関数がbの所有権を持っているのでDropされる
    

    어떤 사인이 좋을까요?


    상기에서 말한 바와 같이 clone의 구성원 중self과 같은 구성원은 이 값을 읽는 상황, 쓴 상황에서 어떤 서명이 좋은지 간단하게 요약할 것이다.

    값을 보다


    ※ T는 모든 종류
  • ( name: Name ) -> &self
  • 값을 읽기만 하면 이렇게 참고를 얻을 수 있다.
    pub fn name(&self) -> &Name {
      &self.name
    }
    
  • ( &T ) -> &self
  • 성능 최적화를 감안하여 T는 지연되어야 한다.정말 복제품을 원하십니까? 호출자에 따라 판단하고 싶습니다.
    pub fn name(&self) -> Name {
      self.name.clone() // 実体が必要かどうかわからない状況で 先に複製を作る…
    }
    
  • ( clone ) -> self
  • &T의 소유권을 빼앗았기 때문에 이 방법의 마지막에 폐기되었다.물론 self도 폐기된다.이 name 의 인용을 되돌릴 수 없어서 컴파일 오류가 발생했습니다.
    pub fn name(self) -> &Name {
      &self.name // コンパイルできない
    } // selfがDropされる
    
  • ( name ) -> self
  • T는 폐기되지만 self는 이동된다.나머지name만 남기고 나머지는 버린 셈이다.이름만 읽고 싶은데 name 찢어지는 건 불편한 것 같아요.
    pub fn name(self) -> Name {
      self.name // nameだけムーブする
    } // selfがDropされる
    
    이 모델은 언제 쉽게 사용할 수 있는지 말하려면self에서 다른 실례가 생겼을 때 사용할 수 있다.예를 들어 구축기의build 방법 등입니다.
    pub fn build(self) -> Request {
      Request::new(self.name, self.group_id)    
    }
    
    self에서 받아들인 후 직접 아무 일도 하지 않으면 폐기된다.따라서 일반적으로 읽기보다는 self에서 다른 값으로 전환하는 것이 좋다.

    값을 매기다


    함수 내부에 모음에 추가하거나 구성원을 대입할 때 소유권을 가져야 합니다.
  • ( self , &mut self )
  • 참조하면&T.처음부터 소유권을 빼앗는 게 좋을 거야.
    pub fn add_name(&mut self, name: &Name) {
      self.names.push(name.clone());
    }
    
  • ( clone , &mut self )
  • 그래서 이게 좋아요.
    pub fn add_name(&mut self, name: Name) {
      self.names.push(name);
    }
    
    구조 구성원이 T이 있다면 실례를 만드는 공장 함수를 사용하여 소유권을 빼앗아야 한다.
    pub fn new(name: Name) -> Person {
      Person {
        name
      }
    }
    
  • ( Name , mut self )
  • 읽기 예시에서도 언급한 바와 같이 &T 되돌아오는 값을 되돌려주지 않으면 버림mut self만 된다.거의 무의미하다.
    pub fn add_name(mut self, name: &Name) {
      self.names.push(name.clone());
    } // selfがDropされる…
    
  • ( self , mut self )
  • 위와 같다.
    pub fn add_name(mut self, name: Name) {
      self.names.push(name);
    } // selfがDropされる…
    
    T의 경우도 변환의 용례가 되겠죠.
    가변 참조할 때만 부르고 싶다면 mut self이 되지만 build0처럼 내부에서 가변할 수 있다.
    pub fn build0(mut self) -> Request {
      self.name = "(" + self.name + ")";
      Request::new(self.name, self.group_id)    
    }
    
    pub fn build1(self) -> Request {
      let mut temp = self;
      temp.name = "(" + self.name + ")";
      Request::new(self.name, self.group_id)    
    }
    
    총결산하면 대체로 도안이 형성된다.Rust의 경우 금형에 대한 정보가 많아 서명만 봐도 내부에서 무슨 일이 일어날지 알 수 있다.
    읽기build1->&self
    pub fn name(&self) -> &Name {
      &self.name
    }
    
    쓰기 &T, &mut self
    pub fn add_name(&mut self, name: Name) {
      self.names.push(name);
    }
    
    공장 상황 쓰기
    pub fn new(name: Name) -> Person {
      Person {
        name
      }
    }
    
    T에서 무엇으로 전환되는 경우
    pub fn build(self) -> Request {
      Request::new(self.name, self.group_id)    
    }
    

    좋은 웹페이지 즐겨찾기