2017-12-13 17 views
0

저는 녹에서 코드 데이 7 강림 대원을하고 있습니다. 그래서 같은 순서가 나무를 구문 분석 할 수 있습니다구조체를 두 위치에 저장하는 방법은 무엇입니까?

c를 말한다
a(10) 
c(5) -> a, b 
b(20) 

가 아이로 ab와 루트입니다.

나는 이것을 처리하기 위해 각 라인을 분석하고, 객체를 만들고, 이름으로 해시에 저장한다. 나중에 a과 같이 자식으로 표시되면 해당 해시를 사용하여 개체를 찾아 아이로 적용 할 수 있습니다. b과 같이 정의되기 전에 나타나는 경우 부분 버전을 만들고 해시를 통해 업데이트 할 수 있습니다. 위의 내용은 다음과 같습니다.

let mut np = NodeParser{ 
    map: HashMap::new(), 
    root: None, 
}; 

{ 
    // This would be the result of parsing "a(10)". 
    { 
     let a = Node{ 
      name: "a".to_string(), 
      weight: Some(10), 
      children: None 
     }; 
     np.map.insert(a.name.clone(), a); 
    } 

    // This is the result of parsing "c(5) -> a, b". 
    // Note that it creates 'b' with incomplete data. 
    { 
     let b = Node{ 
      name: "b".to_string(), 
      weight: None, 
      children: None 
     }; 
     np.map.insert("b".to_string(), b); 

     let c = Node{ 
      name: "c".to_string(), 
      weight: Some(5), 
      children: Some(vec![ 
       *np.map.get("a").unwrap(), 
//    ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content 

       *np.map.get("b").unwrap() 
//    ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content 
      ]) 
     }; 
     np.map.insert(c.name.clone(), c); 
    } 

    // Parsing "b(20)", it's already seen b, so it updates it. 
    // This also updates the entry in c.children. It avoids 
    // having to search all nodes for any with b as a child. 
    { 
     let mut b = np.map.get_mut("b").unwrap(); 
     b.weight = Some(20); 
    } 
} 

노드를보고 해당 노드를 살펴볼 수 있습니다.

// And if I wanted to look at the children of c... 
let node = np.map.get("c").unwrap(); 
for child in node.children.unwrap() { 
//   ^^^^ cannot move out of borrowed content 
    println!("{:?}", child); 
} 

녹이 싫어. NodeParser.mapNode.children은 노드를 소유하고있는 것을 좋아하지 않습니다.

error[E0507]: cannot move out of borrowed content 
    --> /Users/schwern/tmp/test.rs:46:21 
    | 
46 |      *np.map.get("a").unwrap(), 
    |      ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content 

error[E0507]: cannot move out of borrowed content 
    --> /Users/schwern/tmp/test.rs:49:21 
    | 
49 |      *np.map.get("b").unwrap() 
    |      ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content 

그것은 for 루프가 이미 그것을 소유하는 NodeParser에서 노드를 차용했기 때문에 반복 노드를 빌려하려고하는 것을 좋아하지 않는다.

error[E0507]: cannot move out of borrowed content 
    --> /Users/schwern/tmp/test.rs:68:18 
    | 
68 |  for child in node.children.unwrap() { 
    |     ^^^^ cannot move out of borrowed content 

제가 잘못하고있는 것을 이해한다고 생각하지만 올바른 방법을 모르겠습니다.

차용을 행복하게하기 위해 어떻게 구성해야합니까? NodeParser.mapNode.children이 연결되어야하므로 복사가 옵션이 아닙니다.

Here is the code to test with. 실제 코드에서 NodeNodeParser은 모두 구현과 메소드를 가지고 있습니다.

+0

노드에 대한 참조를 직접 저장하는 대신 id (또는이 경우 name)로 저장하고 필요할 때 np.map에서 찾는다. – EvilTak

+0

@EvilTak 제안에 감사드립니다. 그게 효과가있는 동안, 나는 그것보다는 주위에 차용 증서와 함께 작동 무언가를 찾고 있어요. – Schwern

+0

더 쉬운 해결책 ('Rc's를 제외하고)이 존재할 때 왜 그렇게하고 싶은지 확실하지 않습니다. 왜 필요하지 않을 때 빌린 체커와 싸우는가? 차용 증표는 단순히 참조 수명을 초과하는 것을 방지하는 안전 메커니즘 일뿐입니다. 차용을 사용하는 경우에만 차용 증표로 작업하고 싶을 것입니다. – EvilTak

답변

4

한 가지 옵션은 안전하지 않은 코드입니다 ...하지만 당신이 코드의 강림을 사용하여 관용적 인 녹을 배우고 모든 안전을 떨어 뜨리는 것이 아니라면 피하는 것이 좋습니다.

또 다른 옵션은 Node 인스턴스를 참조하여 빌려 오기 검사기가 만족스럽고 컴파일러가 작업을 정리하는 방법을 알고 있도록하는 것입니다. std::rc::Rc 형식은이 작업을 수행하며 ... every call to clone() just increments a reference count을 필수적으로 가져오고 새로운 Rc 인스턴스를 반환합니다. 그런 다음 객체가 삭제 될 때마다 Drop 구현은 참조 횟수를 감소시킵니다.

반복에 관해서는 for x in yfor x in y.into_iter()에 대한 구문 설탕입니다. 이것은 의 내용을 node 밖으로 이동하려고 시도합니다 (the IntoIterator trait, into_iter(self)에있는 내용은 self입니다). 이 문제를 해결하려면 for x in &y을 사용하여 반복 할 때 참조를 요청할 수 있습니다. 이것은 본질적으로 내용을 이동시키지 않는 for x in y.iter()이됩니다.

Here are these suggestions in action.

use std::collections::HashMap; 
use std::rc::Rc; 

struct NodeParser { 
    map: HashMap<String, Rc<Node>>, 
    root: Option<Node>, 
} 

#[derive(Debug)] 
struct Node { 
    name: String, 
    children: Option<Vec<Rc<Node>>>, 
} 

fn main() { 
    let mut np = NodeParser{ 
     map: HashMap::new(), 
     root: None, 
    }; 

    let a = Rc::new(Node{ name: "a".to_string(), children: None }); 
    np.map.insert(a.name.clone(), a.clone()); 

    let b = Rc::new(Node{ name: "b".to_string(), children: None }); 
    np.map.insert(b.name.clone(), b.clone()); 

    let c = Rc::new(Node{ 
     name: "c".to_string(), 
     children: Some(vec![a, b]) 
    }); 
    np.map.insert(c.name.clone(), c.clone()); 

    let node = np.map.get("c").unwrap(); 
    for child in &node.children { 
     println!("{:?}", child); 
    } 
} 

편집 : 여기 내 의견을 확장 해 드리겠습니다.You can use lifetimes here too if you want,하지만 평생 솔루션은 MCVE에 대해 작동하고 실제 문제가에 적용되면 작동하지 않을 것이라고 우려합니다. OP (이 질문뿐만 아니라 다른 것들 ...)도 실제로 있습니다. 수명은 Rust에서 까다 롭고 평생 솔루션이 사람들을 버리게 할 수 있도록 변수의 인스턴스를 다시 주문하는 것과 같은 작은 것들이 있습니다. 내 관심사는 평생 문제로 이어질 것이므로 MCVE에서 작동한다고해도 실제 상황에 맞지 않습니다. 아마 나는 그것을 생각할 것이다.

+0

답변 해 주셔서 감사합니다. 나는 왜 복사가 옵션이 아닌지 설명하기 위해 질문을 업데이트했다. 반복 작업에'&'를 사용하여 설명해 주셔서 감사합니다. 그리고 예, 저는 AoC를 사용하여 관용적 인 녹을 배웁니다. 흠 ... 참조 카운팅은 효과가 있지만 차용자 대신에 일하는 것처럼 보입니다. 어쨌든 나는 그것을 줄 것이다. – Schwern

+1

"복사가 옵션이 아닙니다."- 코드에서'clone()'을 사용했다는 것을 상상해보십시오. 당신이 내 대답을 다시 읽는다면, 여기에서 무슨 일이 일어나고 있는지에 대한 설명을 전합니다. 복제 된 유일한 것은'Rc' 인스턴스입니다. 뒤에서'Rc'는 포인터를 당신이 감싸는 것에 복사합니다 (당신의 자식 노드가 될 것입니다). 여기에'Node' 인스턴스를 복사/복제하는 것이 없습니다. 단지'Rc' 인스턴스 만 복사하면됩니다. 또 하나주의해야 할 점은 "복사"와 "복제"는 개별적으로 녹에서 특정한 것을 의미하며 두 가지를 혼동하면 종종 미묘한 버그가 발생할 수 있다는 것입니다. –

+0

Rc의 작동 방식을 알 수 있습니다. Perl은 참조 횟수를 사용하며 많은 Perl을 수행합니다. 나는 당신이 당신의 대답을 적극적으로 편집하고있는 것을 보았을 때 문제에 대한 업데이트를 당신에게 알리고 싶었습니다. 평생 동안이 문제를 해결할 수 있을까요? 나는 그들을 이해하려고 노력하고있다. – Schwern