Resumo : sou novo no Rust, então decidi praticar implementando uma lista duplamente vinculada. Para fins de depuração, implementei o get()
método, mas não consegui copiar o valor de um arquivo Rc<RefCell<_>>
. (Desculpe por fazer pergunta idiota)
Problema : Estou tentando retornar um Result<T, &'static str>
in .get()
onde T
é o tipo de dados armazenados no nó e &str
é a string da mensagem de erro. O verificador de empréstimo me diz que não posso retornar uma referência a uma variável do método, então tentei copiar o valor interno e devolvê-lo, mas não consegui.
Código fonte :
use std::{rc::Rc, cell::RefCell};
struct Node<T> {
data: Option<T>,
prev: Option<Rc<RefCell<Node<T>>>>,
next: Option<Rc<RefCell<Node<T>>>>,
}
impl<T> Node<T> {
/// Instantiate a new dummy node.
/// This node is used to mark the start and end of the list.
/// It is not counted in the size of the list.
fn new() -> Self {
Node {
data: None,
prev: None,
next: None,
}
}
/// Instantiate a new content node.
/// This node is used to store data.
/// It is counted in the size of the list.
fn from(data: T) -> Self {
Node {
data: Some(data),
prev: None,
next: None,
}
}
}
struct List<T> {
head: Rc<RefCell<Node<T>>>,
tail: Rc<RefCell<Node<T>>>,
size: usize,
}
impl<T> List<T> {
pub fn new() -> Self {
let head = Rc::new(RefCell::new(Node::new()));
let tail = Rc::new(RefCell::new(Node::new()));
head.borrow_mut().next = Some(Rc::clone(&tail));
tail.borrow_mut().prev = Some(Rc::clone(&head));
List { head, tail, size: 0 }
}
pub fn prepend(&self, data: T) {
let node = Rc::new(RefCell::new(Node::from(data)));
let mut head = self.head.borrow_mut();
node.borrow_mut().next = Some(head.next.take().unwrap());
node.borrow_mut().prev = Some(Rc::clone(&self.head));
head.next = Some(Rc::clone(&node));
if let Some(next) = node.borrow().next.as_ref() {
next.borrow_mut().prev = Some(Rc::clone(&node));
};
}
pub fn append(&self, data: T) {
let node = Rc::new(RefCell::new(Node::from(data)));
let mut tail = self.tail.borrow_mut();
node.borrow_mut().prev = Some(Rc::clone(&tail.prev.take().unwrap()));
node.borrow_mut().next = Some(Rc::clone(&self.tail));
tail.prev = Some(Rc::clone(&node));
if let Some(prev) = node.borrow().prev.as_ref() {
prev.borrow_mut().next = Some(Rc::clone(&node));
};
}
pub fn get(&self, index: isize) -> Result<T, &'static str> {
let mut current: Rc<RefCell<Node<T>>> = Rc::clone(self.head.borrow().next.as_ref().unwrap());
for _ in 0..index {
let tmp = Rc::clone(current.borrow().next.as_ref().ok_or("Index out of range")?);
current = tmp;
}
let result = current.borrow().data.as_ref().ok_or("Index out of range")?; // error[E0716]
Ok(*result) // error[E0507]
}
}
/*
error[E0716]: temporary value dropped while borrowed
--> src\linked.rs:74:22
|
74 | let result = current.borrow().data.as_ref().ok_or("Index out of range")?;
| ^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary value which is freed while still in use
75 | Ok(*result)
| ------- borrow later used here
|
help: consider using a `let` binding to create a longer lived value
|
74 ~ let binding = current.borrow();
75 ~ let result = binding.data.as_ref().ok_or("Index out of range")?;
|
error[E0507]: cannot move out of `*result` which is behind a shared reference
--> src\linked.rs:75:12
|
75 | Ok(*result)
| ^^^^^^^ move occurs because `*result` has type `T`, which does not implement the `Copy` trait
*/
Eu tentei :
- Este post é sobre como fazer uma referência em value , mas é isso que estou tentando fazer e falhei.
- Este post sobre como modificar um
RefCell
, mas isso não ajudou. Eu estava tentando devolvê-lo, não transformá-lo. - Este post é sobre como pegar um empréstimo
RefCell
, mas não consigo devolver o valor emprestado porque o emprestadoRc
tem vida curta (mas o valor interno não). - Este post sobre como retornar algo dentro de a
RefCell
e este post sobre como devolvê-lo com.map()
, mas o compilador diz "trait vinculado não satisfeito" quando tentei usar.into()
e emprestar o verificador reclama "não é possível sair" se eu remover o.into()
. - Este post é sobre o uso de
Rc::try_unwarp()
, mas não funcionará, pois os dados internos têm mais de um proprietário.
Além disso : posso ter feito isso errado, por favor, perdoe-me se uma das postagens resolveu meu problema, mas eu não o implementei da maneira certa e, por favor, me ensine como fazê-lo corretamente. Muito obrigado.
Você não.
Se o valor for
Copy
, você poderá copiá-lo. Se forClone
, você pode cloná-lo. Mas se não for, ou se for muito caro clonar?Você está sem sorte.
A mutabilidade interna (como
RefCell
) é ruim para APIs. Ele tende a se propagar através dos limites da API e, às vezes, até impede que você os implemente. É bom (às vezes) dentro da implementação, mas não porque vaza.É claro que existe uma maneira adequada de implementar uma lista duplamente vinculada (já que std possui uma ), mas essa maneira adequada requer código inseguro. Você pode querer voltar a ele quando já tiver dominado o Rust, como um exercício. E certifique-se de ler Aprenda sobre ferrugem com muitas listas vinculadas .
Isso funcionou para mim:
( link do parque infantil )
Você precisa da linha
where T: Clone
para ativar o.clone()
método.Alguns dizem que é um erro implementar uma lista duplamente vinculada para aprender Rust... Lamento informar que eles estão certos. Eu fiz exatamente a mesma coisa quando comecei. A outra coisa que tentei foi escrever um raytracer; isso foi muito melhor.
As estruturas de dados principais são implementadas usando ponteiros brutos. Isso significa escrever código inseguro e colocar uma API segura em torno dele: uma habilidade avançada em Rust.