Rust의 Smart point ers는 무엇입니까?
63785 단어 Rust순환 참조smartpointertech
개요
요즘 내 공부는 전혀 진전이 없어...
이런 약음을 말하면서 틈틈이 THE BOOK의 후속 내용을 계속 읽는다.
글이 너무 많아서 그런지 Ownerrship, Lifetime에 이어 하나도 안 나왔어요.
Smart pointers 로깅
Smart pointers
Pointer란
&
에 표시된 참조 같은 것을 말합니다.Smart가 첨부되어 있어서 참고뿐만 아니라 기능도 있는 것 같아요.
귀속 구조에 도움이 되는 Box<T>
불순한 상황
리스트 중에 리스트가 있고 그 중에서도 리스트가 있고 재기가 지속되는 경우도 있어요.
코드로 표현하면 이런 느낌이에요.
use List::{Cons, Nil};
#[derive(Debug)]
enum List {
Cons(i32, List),
Nil,
}
fn main() {
let list = Cons(1, Cons(2, Cons(3, Nil)));
println!("list = {:?}", list);
}
이걸 컴파일하면 이런 오류가 발생합니다... Compiling playground v0.0.1 (/playground)
error[E0072]: recursive type `List` has infinite size
--> src/main.rs:3:1
List의 enum형 스토리지 영역을 확보하려는 경우Cons의 List의 Cons의 스토리지 영역
이렇게 끝없이 쫓아가면 상술한 착오가 생길 것이다
Box<T> 컴파일 허용
상기 재기성 구조를 실현하기 위해 사용된 것은
Smaprt Point의 사람이 있음
Box<T>
use List::{Cons, Nil};
#[derive(Debug)]
enum List {
Cons(i32, Box<List>),
Nil,
}
fn main() {
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
println!("list = {:?}", list);
}
Box<T>
pointer이기 때문에 실제 저장된 데이터 양이 아닙니다포인트에 사용할 데이터의 양만 알면 돼요.
따라서 데이터 양을 재조사하지 않고 첫 번째
Box<T>
에 필요한 데이터 양을 계산합니다.전항의 번역 오류를 돌파할 수 있다
여러 소유권의 RC<T>
불순한 상황
가까스로
Box<T>
귀속성을 표현할 수 있는 구조그리고 컴파일 오류가 발생했습니다.
이렇게 여러 변수를 유지하는 Ownnersip의 경우.
코드로 하면 이런 느낌.
use List::{Cons, Nil};
#[derive(Debug)]
enum List {
Cons(i32, Box<List>),
Nil,
}
fn main() {
let a = Cons(5, Box::new(Cons(10, Box::new(Nil))));
let b = Cons(3, Box::new(a));
let c = Cons(4, Box::new(a));
println!("a = {:?}", a);
println!("b = {:?}", b);
println!("c = {:?}", c);
}
이런 컴파일 오류가 발생할 수 있습니다 Compiling playground v0.0.1 (/playground)
error[E0382]: use of moved value: `a`
--> src/main.rs:12:30
let b = Cons(3, Box::new(a));
a의ownership에서 이동하고 a는movelet c = Cons(4, Box::new(a));
여기서 혼났어요.😭RC<T>로 해결
RC<T>
여러 개의 Ownnership을 허용하기 때문에 이 상황에서 사용할 수 있습니다!Ownnership을 뺏는 게 아니라 공유하는 이미지입니다.
use std::rc::Rc;
use List::{Cons, Nil};
#[derive(Debug)]
enum List {
Cons(i32, Rc<List>),
Nil,
}
fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
let b = Cons(3, Rc::clone(&a));
let c = Cons(4, Rc::clone(&a));
println!("a = {:?}", a);
println!("b = {:?}", b);
println!("c = {:?}", c);
}
RC<T>에 여러 개의 Ownnership이 저장되어 있는지 확인
사용
Rc::strong_count(&a)
후모두 몇 개의 Ownerrship이 있는지 확인할 수 있습니다 (참조 계수 있음)
use std::rc::Rc;
use List::{Cons, Nil};
#[derive(Debug)]
enum List {
Cons(i32, Rc<List>),
Nil,
}
fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
println!("count after creating a = {}", Rc::strong_count(&a));
// 最初は参照カウント1
let _b = Cons(3, Rc::clone(&a));
println!("count after creating b = {}", Rc::strong_count(&a));
// cloneしたので参照カウントが2になる
{
let _c = Cons(4, Rc::clone(&a));
println!("count after creating c = {}", Rc::strong_count(&a));
// cloneしたので参照カウントが3になる
}
println!("count after c goes out of scope = {}", Rc::strong_count(&a));
// ここでDropされるので参照カウントが1減って2になる
} // 参照カウントが0になるのでaは完全に破棄される
모바일 기기의 내부 가변성을 지원하는 RefCell<T>
불순한 상황
이런 느낌으로 이루어져도
#![allow(unused)]
fn main() {
pub trait Messenger {
fn send(&self, msg: &str);
}
pub struct LimitTracker<'a, T: 'a + Messenger> {
messenger: &'a T,
value: usize,
max: usize,
}
impl<'a, T> LimitTracker<'a, T>
where
T: Messenger,
{
pub fn new(messenger: &T, max: usize) -> LimitTracker<T> {
LimitTracker {
messenger,
value: 0,
max,
}
}
pub fn set_value(&mut self, value: usize) {
self.value = value;
let percentage_of_max = self.value as f64 / self.max as f64;
if percentage_of_max >= 0.75 && percentage_of_max < 0.9 {
// 警告: 割り当ての75%以上を使用してしまいました
self.messenger
.send("Warning: You've used up over 75% of your quota!");
} else if percentage_of_max >= 0.9 && percentage_of_max < 1.0 {
// 切迫した警告: 割り当ての90%以上を使用してしまいました
self.messenger
.send("Urgent warning: You've used up over 90% of your quota!");
} else if percentage_of_max >= 1.0 {
// エラー: 割り当てを超えています
self.messenger.send("Error: You are over your quota!");
}
}
}
}
send
방법을 테스트 코드로 Mock으로 설정할 때 이런 느낌이 든다#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
use super::*;
struct MockMessenger {
sent_messages: Vec<String>,
}
impl MockMessenger {
fn new() -> MockMessenger {
MockMessenger {
sent_messages: vec![],
}
}
}
impl Messenger for MockMessenger {
fn send(&self, message: &str) {
self.sent_messages.push(String::from(message));
}
}
#[test]
fn it_sends_an_over_75_percent_warning_message() {
let mock_messenger = MockMessenger::new();
let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);
limit_tracker.set_value(80);
assert_eq!(mock_messenger.sent_messages.len(), 1);
}
}
}
그런데 이걸 실행하면 컴파일 오류가 발생할 수 있어요.error[E0596]: cannot borrow immutable field `self.sent_messages` as mutable
(エラー: 不変なフィールド`self.sent_messages`を可変で借用できません)
--> src/lib.rs:52:13
fn send(&self, message: &str) {
와 같이self는 변하지 않는 참조그럼
&mut self
하죠.이렇게 되면 메시지의 트레이트와 많이 달라서 할 수 없어...
이때 내부 가변성
RefCell<T>
을 사용할 수 있다RefCell<T>로 해결
#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
struct MockMessenger {
sent_messages: RefCell<Vec<String>>,
}
impl MockMessenger {
fn new() -> MockMessenger {
MockMessenger {
sent_messages: RefCell::new(vec![]),
}
}
}
impl Messenger for MockMessenger {
fn send(&self, message: &str) {
self.sent_messages.borrow_mut().push(String::from(message));
}
}
#[test]
fn it_sends_an_over_75_percent_warning_message() {
// --snip--
let mock_messenger = MockMessenger::new();
let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);
limit_tracker.set_value(75);
assert_eq!(mock_messenger.sent_messages.borrow().len(), 1);
}
}
}
RefCell<Vec<String>>
에서 정의유지
&self
상태에서 변경되지 않는 참조를 변경할 수 있습니다.구체적으로
borrow_mut()
로 바꿀 수 있습니다!이런 Mock 상황에서는 효과가 있는 것 같아요.🤩
참조 주기 주의
원형 참조가 되는 경우
교정에 붙이다
use std::cell::RefCell;
use std::rc::Rc;
use List::{Cons, Nil};
#[derive(Debug)]
enum List {
Cons(i32, RefCell<Rc<List>>),
Nil,
}
impl List {
fn tail(&self) -> Option<&RefCell<Rc<List>>> {
match *self {
Cons(_, ref item) => Some(item),
Nil => None,
}
}
}
fn main() {
let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));
println!("a initial rc count = {}", Rc::strong_count(&a));
// aの最初の参照カウント = 1
println!("a next item = {:?}", a.tail());
// aの次の要素は = Some(RefCell { value: Nil })
let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));
println!("a rc count after b creation = {}", Rc::strong_count(&a));
// b作成後のaの参照カウント = 2
println!("b initial rc count = {}", Rc::strong_count(&b));
// bの最初の参照カウント = 1
println!("b next item = {:?}", b.tail());
// bの次の要素 = Some(RefCell { value: Cons(5, RefCell { value: Nil }) })
if let Some(link) = a.tail() {
*link.borrow_mut() = Rc::clone(&b);
}
// aのtailにbをセットする
println!("b rc count after changing a = {}", Rc::strong_count(&b));
// aを変更後のbの参照カウント = 2
println!("a rc count after changing a = {}", Rc::strong_count(&a));
// aを変更後のaの参照カウント = 2
// Uncomment the next line to see that we have a cycle;
// it will overflow the stack
// 次の行のコメントを外して循環していると確認してください; スタックオーバーフローします
// println!("a next item = {:?}", a.tail()); // aの次の要素 = {:?}
}
내부 가변성RefCell<T>
과 여러 개의 소유권을 허용하는RC<T>
를 조합하면순환 참조 생성
상기 상황에서 마지막 논평을 없애면 순환 참조, 무한 출력
주석을 삭제하지 않으면 컴파일 자체가 가능합니다
main의 마지막에 b, a가 버려지고 참조 계수는 2->1
참조 수가 0이 아니기 때문에 데이터는 버려지지 않고 메모리에 남아 있습니다...
따라서 원형 참조는 좋지 않습니다.이렇게 하면
Weak<T>로 회피
Weak<T>
사용 후 글씨체와 같이 약한 참고가 될 수 있다순환 참조를 만들어도 전항과 같은 상황은 일어나지 않는다.
이전 장의 List를 노드로 바꿉니다.
부모와 약한 참조(
Weak<T>
, 자녀와 강한 참조Rc<T>
branch의child 참조leafleaf의parent 참조branch 제작 순환 참조
Weak의 인용이 0이 안 되더라도 강력한 참조 수가 0이 되면 데이터는 사라져요
이러한 특성을 이용하여 상술한 순환 참조 문제 참조는 발생하지 않습니다!!
뭐, 이런 계층 구조나 순환이 일어나는 상황에서 사용해 보세요!이런 느낌 있잖아요.
use std::cell::RefCell;
use std::rc::{Rc, Weak};
#[derive(Debug)]
struct Node {
value: i32,
parent: RefCell<Weak<Node>>,
child: RefCell<Option<Rc<Node>>>,
}
fn main() {
let leaf = Rc::new(Node {
value: 3,
parent: RefCell::new(Weak::new()),
child: RefCell::new(None),
});
// leafの親 = {:?}
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
let branch = Rc::new(Node {
value: 5,
parent: RefCell::new(Weak::new()),
child: RefCell::new(Some(Rc::clone(&leaf))),
});
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
}
총결산
한 번만 보면 도무지 알아듣지 못할 것 같다
여러 번 다시 읽었더니 답답하다.
어쩔 수 없이 사용하게 된 상태에서 다시 돌아보도록 하겠습니다.
Reference
이 문제에 관하여(Rust의 Smart point ers는 무엇입니까?), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/ucwork/articles/4fc4cfa47cda26텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)