클래스 대 유형(또는 Rust를 배워야 하는 또 다른 이유)
소개
참고: 이것은 do classes suck 또는 do classes suck 기사에 대한 후속 조치이며 learn Rust 해야 하는 또 다른 이유이므로 이 게시물의 컨텍스트를 이해하기 위해 적어도 하나는 읽어야 합니다. .
사양으로 돌아가기
참고로 사양은 다음과 같습니다.
참고로 사양은 다음과 같습니다.
녹 사용
Rust의 목표는 코드가 한 번에 3가지가 되도록 하는 것입니다: 정확하고 빠르며 표현력이 좋습니다. 이것은 쉬운 작업이 아니며 Rust를 배우기가 약간 복잡한 이유입니다(제 생각에는 그렇게 나쁘지는 않지만 이야기를 뺍니다).
어쨌든 Rust 프로그래밍 언어에서 흥미로운 점은 유효하지 않은 상태가 컴파일 오류로 이어지고 구조체와 메서드만 사용하는 방식으로 사양을 표현할 수 있다는 것입니다.
방법은 다음과 같습니다. 사양은 존재하거나 존재하지 않을 수 있는 이름에 대해 이야기하므로 두 가지 유형을 사용합니다. 하나는 이름이 없는 로봇용이고 다른 하나는 이름이 있는 로봇용입니다. 정확히 말하면 구조체라고 합니다.
// We wrap the robot name inside a struct
pub struct NamedRobot {
name: String,
}
// We create a brand new type for "robots that don't have names"
pub struct UnnamedRobot;
// Note : interestingly, this struct costs *nothing* to allocate and is
// known as a zero-sized type (ZST) in Rust parlance.
그런 다음 허용된 상태 간의 전환을 해당 유형의 공용 메서드(impl
블록 내부) 또는 자유 함수로 표현할 수 있습니다.
참고: 대부분의 본문은 여기에서 생략되었지만 전체 소스 코드를 찾을 수 있습니다on github.
// Note: this is a private implementation detail, so no
// `pub` here!
fn generate_random_name() -> String { /* ... */ }
pub fn new_robot() -> UnnamedRobot { /* ... */ }
impl UnnamedRobot {
// Note: emit a compiler warning if users call start() without
// using the return value
#[must_use]
pub fn start(self) -> NamedRobot {
let name = generate_random_name();
NamedRobot { name }
}
}
impl NamedRobot {
pub fn name(&self) -> &str { /* ... */ }
pub fn stop(&self) { /* ... */ }
pub fn start(&self) { /* ... */ }
// Note: taking ownership of `self` here!
pub fn reset(self) -> UnnamedRobot { /* ... */ }
}
물론 유효한 코드 컴파일:
let robot = new_robot();
let robot = robot.start();
let name = robot.name();
println!("New robot with name: {name}");
유효하지 않은 코드는 컴파일되지 않습니다. 예를 들어:
let robot = new_robot();
let name = robot.name()
// Error: method name() not found for UnnamedRobot
로봇을 재설정하고 다시 시작하여 새 이름을 얻을 수 있습니다.
let robot = new_robot();
let robot = robot.start();
let name1 = robot.name().to_string();
let robot = robot.reset();
let robot = robot.start();
let name2 = robot.name().to_string();
assert_ne!(name1, name2);
왜 여기에 .to_string()
가 필요한지 궁금할 것입니다. 음, 설명하겠습니다.
소유와 차용
앞에서 reset
가 로봇의 소유권을 갖는다고 언급했습니다.
메서드가 self
를 첫 번째 매개변수로 사용하기 때문입니다.
이에 반해 name
방식은 &self
를 이용하여 로봇을 빌린다.
위에서 본 것처럼 풍부한 유형 시스템을 갖는 것 외에도 Rust 컴파일러는 소유권에 대한 규칙도 시행하기 때문에 이것은 중요합니다.
다음은 해당 규칙입니다.
// We wrap the robot name inside a struct
pub struct NamedRobot {
name: String,
}
// We create a brand new type for "robots that don't have names"
pub struct UnnamedRobot;
// Note : interestingly, this struct costs *nothing* to allocate and is
// known as a zero-sized type (ZST) in Rust parlance.
// Note: this is a private implementation detail, so no
// `pub` here!
fn generate_random_name() -> String { /* ... */ }
pub fn new_robot() -> UnnamedRobot { /* ... */ }
impl UnnamedRobot {
// Note: emit a compiler warning if users call start() without
// using the return value
#[must_use]
pub fn start(self) -> NamedRobot {
let name = generate_random_name();
NamedRobot { name }
}
}
impl NamedRobot {
pub fn name(&self) -> &str { /* ... */ }
pub fn stop(&self) { /* ... */ }
pub fn start(&self) { /* ... */ }
// Note: taking ownership of `self` here!
pub fn reset(self) -> UnnamedRobot { /* ... */ }
}
let robot = new_robot();
let robot = robot.start();
let name = robot.name();
println!("New robot with name: {name}");
let robot = new_robot();
let name = robot.name()
// Error: method name() not found for UnnamedRobot
let robot = new_robot();
let robot = robot.start();
let name1 = robot.name().to_string();
let robot = robot.reset();
let robot = robot.start();
let name2 = robot.name().to_string();
assert_ne!(name1, name2);
앞에서
reset
가 로봇의 소유권을 갖는다고 언급했습니다.메서드가
self
를 첫 번째 매개변수로 사용하기 때문입니다.이에 반해
name
방식은 &self
를 이용하여 로봇을 빌린다.위에서 본 것처럼 풍부한 유형 시스템을 갖는 것 외에도 Rust 컴파일러는 소유권에 대한 규칙도 시행하기 때문에 이것은 중요합니다.
다음은 해당 규칙입니다.
이것이 의미하는 바는 로봇이 시작될 때 수정하려고 시도하지 않는 한 언제든지 로봇 이름에 액세스할 수 있다는 것입니다. 그러나
reset()
를 호출하면 값이 이동되어 더 이상 로봇 이름에 접근할 수 없습니다.실제로 해봅시다:
let robot = new_robot();
let robot = robot.start();
let name1 = robot.name().to_string();
let name2 = robot.name().to_string();
robot.reset();
let name3 = robot.name();
// Error: value `robot` moved during called to reset reset()
Rust가 이 코드를 컴파일하도록 허용했다면 로봇이 재설정된 후
name3
변수가 생겼을 것입니다. 이는 사양에서 허용되지 않습니다!이는 또한
name1
및 name2
가 단순한 문자열 참조( &str
)일 수 없음을 의미합니다.그래서 우리는
.to_string()
메서드를 사용합니다. 이 메서드는 로봇 이름에 대해 가변 복사본(a String
)을 제공합니다.결론
Rust를 배우면 다음을 찾을 수 있습니다.
참고 사항: 요점을 설명하기 위해 두 가지 다른 유형을 사용했습니다. aptly named “idiomatic” branch on GitHub에서 코드의 관용적 버전을 찾을 수 있습니다.
빌린 검사기 규칙이 컴파일 시간에 유효하지 않은 상태를 방지하는 방법을 다시 보여줍니다. 이번에는 (
start
또는 reset
와 같은 일부 메서드가 &mut self
를 사용하고 다른 메서드는 &self
(name
와 같은)를 사용하기 때문입니다. 또한 런타임에 로봇 이름 형식을 적용하기 위해 "새로운 유형"패턴을 보여줍니다.귀하의 의견을 듣고 싶습니다. 아래에 의견을 남겨 주시거나 제 contact page에서 저와 연락할 수 있는 더 많은 방법을 확인하십시오.
Reference
이 문제에 관하여(클래스 대 유형(또는 Rust를 배워야 하는 또 다른 이유)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/dmerejkowsky/classes-vs-types-or-yet-another-reason-to-learn-rust-208h텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)