Rust로 도메인 고유형을 만들 때의 비결

39885 단어 Rusttech
Rust로 도메인 고유, 도메인 객체를 구현할 때의 노하우를 간략하게 요약했습니다.뭐,'역고유형'하면 좀 오버니까'나 혼자 만드는 형'만 생각하면 될 것 같아.OOP에 익숙한 사람들이 쉽게 빠져드는 부분도 간단히 언급했다.참고로 제공하다.

통신부 형식 만들기


주소록 형식을 예로 들다.
우선 코드는 아래와 같다.코드 전체는 참조GiitHub 웨어하우스.
#[derive(Debug, Clone)]
pub struct AddressBook {
  name: String,
  entries: Vec<AddressEntry>,
}

impl Default for AddressBook {
  fn default() -> Self {
    Self {
      name: String::default(),
      entries: Vec::default(),
    }
  }
}

impl AddressBook {
  
  pub fn new(name: &str) -> Self {
    Self {
      name: name.to_owned(),
      entries: Vec::default(),
    }
  }

  pub fn name(&self) -> &str {
    &self.name
  }

  pub fn add_entry(&mut self, address_entry: AddressEntry) {
    self.entries.push(address_entry);
  }

  pub fn remove_entry(&mut self, address_entry_id: AddressEntryId) -> AddressEntry {
    let index = self
      .entries
      .iter()
      .position(|e: &AddressEntry| e.id == address_entry_id)
      .unwrap();
    self.entries.remove(index)
  }

  pub fn iter(&self) -> impl Iterator<Item = &AddressEntry> {
    self.entries.iter()
  }
}
사용 방법은 다음과 같다.같은 변수 이름을 재사용했지만 Rust에서 컴파일 오류가 발생하지 않습니다.그림자
let mut address_book = AddressBook::new("社員名簿");
    
let address_entry_id = AddressEntryId::new(1);
let personal_name = PersonName::new("Junichi", "Kato");
let address = Address::new("111-0001", "Tokyo-to", "minato-ku 1", Some("hoge 1 building"));
let address_entry = AddressEntry::new(address_entry_id, personal_name, address);
address_book.add_entry(address_entry);

let address_entry_id = AddressEntryId::new(2);
let personal_name = PersonName::new("Taro", "Yamamoto");
let address = Address::new("111-0002", "Tokyo-to", "minato-ku 2", Some("hoge 2 building"));
let address_entry = AddressEntry::new(address_entry_id, personal_name, address);
address_book.add_entry(address_entry);

let address_entry_id = AddressEntryId::new(3);
let personal_name = PersonName::new("Hanako", "Yamada");
let address = Address::new("111-0003", "Tokyo-to", "minato-ku 3", Some("hoge 3 building"));
let address_entry = AddressEntry::new(address_entry_id, personal_name, address);
address_book.add_entry(address_entry);

println!("name = {}", address_book.name())
address_book.iter().for_each(|e| println!("{:?}", e));

/* 出力例
name = 社員名簿
AddressEntry { id: AddressEntryId(1), name: PersonName { first_name: "Junichi", last_name: "Kato" }, address: Address { postal_code: "111-0001", pref: "Tokyo-to", address: "minato-ku 1", building: Some("hoge 1 building") } }
AddressEntry { id: AddressEntryId(2), name: PersonName { first_name: "Taro", last_name: "Yamamoto" }, address: Address { postal_code: "111-0002", pref: "Tokyo-to", address: "minato-ku 2", building: Some("hoge 2 building") } }
AddressEntry { id: AddressEntryId(3), name: PersonName { first_name: "Hanako", last_name: "Yamada" }, address: Address { postal_code: "111-0003", pref: "Tokyo-to", address: "minato-ku 3", building: Some("hoge 3 building") } }
*/

구조 정의


클래스 정의의 느낌으로 구조체를 정의하자.구조체 내부의 정의는 속성의 구성원에 해당한다.
#[derive(Debug, Clone)]
pub struct AddressBook {
  name: String,
  entries: Vec<AddressEntry>,
}
구성원에게 이 주소록의 이름을 가지게 한다.&str,String도대체 어떤 사람인지 다음 기사를 읽어보세요.String형.
https://qiita.com/Kogia_sima/items/6899c5196813cf231054
주소 정보를 저장하는 AddressEntry 유형을 따로 제작한다.의존형도 적당히 만든다.
#[derive(Debug, Clone)]
pub struct AddressEntry {
  pub id: AddressEntryId,
  pub name: PersonName,
  pub address: Address,
}
AddressBook는 여러 개AddressEntry를 보존하기 때문에 entriesVec<AddressEntry>로 정의했다.길이가 고정되면 배열이 가능하지만 고정되지 않으면 선택Vec.

플랜트(관련 함수)


인스턴스를 생성할 때는 다음과 같이 설명할 수 있습니다.그러나 멤버가 표시되어야 합니다.
let address_book1 = AddressBook {
  name: "従業員名簿".to_owned(),
  entries: Vec::default(),
};
개인 구성원일 때 구조기처럼 작용하는 관련 함수를 정의한다.
  pub fn new(name: &str) -> Self {
    Self {
      name: name.to_owned(),
      entries: Vec::default(),
    }
  }
는 정태적인 방법과 유사한 것으로 볼 수 있다.
let address_book1 = AddressBook::new("従業員名簿");
Rust는 동일한 함수 이름으로 여러 매개변수 모드를 정의할 수 없습니다(과부하할 수 없음).기본적으로 각각 다른 이름을 지어야 한다.
추기trait를 사용하면 과부하가 가능합니다.뭐, 다상성을 추구하지 않는데도 trait 괜찮겠어기술량이 늘었기 때문에 좋고 나쁨이 있다.그렇다면 각자 알기 쉬운 이름을 지어주는 게 좋을 것 같아서...
https://ubnt-intrepid.hatenablog.com/entry/2016/09/21/063652
https://qiita.com/smicle/items/235fb6e3ead86c2b03f3

정의 방법


첫 번째 파라미터에서 수신기를 설명하는 방법을 정의합니다.
pub fn name(&self) -> &str {
  &self.name
}
&self와 같다고 생각하세요self: &AddressBook(뜻이 같아도 쓰지 마세요self: &AddressBook.
이 방법은 되돌아갈 수 밖에 없기 때문에 다음과 같은 내용을 쓸 수 있다.
pub fn name(&self) -> String {
  self.name.clone()
}
움직이는 더러운 코드를 쓸 때는 괜찮겠지.하지만 쓸모없는 자리가 생겼다.이 경우 name의 참고로 되돌아갈 수 있으며 클론에서 복제되었는지 여부는 호출자에게 맡겨야 합니다.

처리 가변성

self.name 불변 참조이므로 상태를 변경할 수 없습니다.상태&self를 변경하려면 &mut self에 요소를 추가할 수 있습니다.
pub fn add_entry(&mut self, address_entry: AddressEntry) {
  self.entries.push(address_entry);
}
"어, 변형 가능한가요? 이제 안 변하죠?"이렇게 생각하는 사람.
https://doc.rust-jp.rs/book-ja/ch04-02-references-and-borrowing.html#가변 참조
같이 읽읍시다.
특정한 범위 내에서 특정한 데이터에 대해 하나의 가변적인 참고만 있을 수 있다.
또한 인용할 때 가변 인용을 사용할 수 없다.
Rust의 가변성은 안전하게 처리할 수 있다.
정의된 서명을 통해 알 수 있듯이 가변 참조가 아닌 경우에는 가변 작업을 할 수 없습니다.변경되지 않을 때 가변 작업을 하면 컴파일 오류가 발생합니다.
let mut address_book = AddressBook::new("社員名簿");

address_book.add_entry(address_entry); // &mut self
let address_book = AddressBook::new("社員名簿");

address_book.add_entry(address_entry); // &selfはコンパイルエラー。そんなメソッドはない。
자바 등 언어로 등급을 바꾸면 속성이 많은 반은 따로 빌딩을 만들지만 Rust는 같은 유형으로 변하지 않고 변할 수 있다.self.entries를 매개변수로 하는 Rust이므로 가능합니다.

한 번도 안 변할 수 있는데...


위치를 개의치 않는다면 아래와 같이 하십시오.
pub fn add_entry(self, address_entry: AddressEntry) -> Self {
  let mut new_self = self;
  new_self.entries.push(address_entry);
  Self {
    ..new_self
  }
}
수정된 새로운 실례를 생성할 수 있습니다.기본적으로 원래의 실례를 사용하지 않고 필요할 때self만 있으면 되기 때문에 아니다clone면 충분하다.
let address_book = AddressBook::default();
let address_book = address_book.add_entry(address_entry);
// 更新前のインスタンスを残したいなら、呼び出し元でclone()すればいい
// let address_book_updated = address_book.clone().add_entry(address_entry);
다양한 설치를 읽었지만 이런 설치는 보기 드물다.기본적으로 불변 인용을 사용하면 부작용을 억제할 수 있지만 가변은 좁은 범위에서 사용하고 제어를 없애면 불변을 회복할 수 있다.

Vec 대신 슬라이스를 매개변수로 사용


Rust에는 가변 길이 매개변수가 없습니다.동일한 유형의 매개변수를 여러 개 가져올 때는 Vec 또는 슬라이스입니다.다음 글을 읽으십시오.
https://qiita.com/mosh/items/709effc9e451b9b8a5f4 &self형은 괜찮은 것 같아요.
pub fn add_entries(&mut self, address_entries: Vec<AddressEntry>) {
슬라이스에 대한 참조를 받는 것이 편리합니다.이번에는 요소형 실체selfVecto_vecVec으로 바뀌어야 하기 때문에 위치가 발생한다.(이것이 성능에 문제가 있다면 into_iter 이동이 가능할 수도 있습니다...)
pub fn add_entries(&mut self, address_entries: &[AddressEntry]) {
  address_entries
    .to_vec()
    .into_iter()
    .for_each(|e: AddressEntry| self.add_entry(e))
}
Vec 또는 슬라이드를 사용하여 값 집합을 추출할 수 있습니다.
let mut address_book = AddressBook::new("社員名簿");

let entries = [address1.clone(), address2.clone()]; // 配列
address_book.add_entries(&entries); // スライスの参照

let entries = vec![address1, address2]; // Vec
address_book.add_entries(&entries); // Derefの実装がスライスの参照を返す
보충:
https://b.hatena.ne.jp/entry/4707991669921759874/comment/Magicant
깨우쳐 주셔서 감사합니다.
그래서 나는 즉각 실현해 보았다.
pub fn add_entries1(&mut self, address_entries: impl IntoIterator<Item=AddressEntry>) {
  address_entries
      .into_iter()
      .for_each(|e| self.add_entry(e))
}
  
let entries1= [address_entry1.clone(), address_entry2.clone()];
address_book.add_entries1(entries1);

let entries2= vec![address_entry1, address_entry2];
address_book.add_entries1(entries2);  
슬라이스 참조로 수신address_entries: Vec<AddressEntry>하면 위치가 생기기 때문에 이쪽을 움직일 수 있어 효율이 높습니다.

좋은 웹페이지 즐겨찾기