Имя типа T не определено при создании изменяемого двоичного дерева

#pointers #data-structures #tree #rust #binary-tree

#указатели #структуры данных #дерево #Ржавчина #двоичное дерево

Вопрос:

Я новичок в Rust, и для упражнения я создаю простое универсальное двоичное дерево. Вот как я бы создал его на C

 template<typename T>
struct Node
{
    T data;
    Node<T>* parent;
    Node<T>* left;
    Node<T>* right;
};

template<typename T>
struct Bintree
{
    Node<T>* root;  
};
  

Но тот же самый (иш) код в Rust, похоже, не работает:

 use std::ptr;

struct Node<T> {
    data: T,
    left: amp;Node<T>,
    right: amp;Node<T>,
    parent: amp;Node<T>,
}

struct Tree<T> {
    root: amp;Node<T>,
}

impl Tree<T> {
    pub fn new() -> Tree<T> {
        Tree { root: ptr::null() }
    }

    pub fn insert(amp;self, value: T) {
        if root.is_null() {
            self.root = Node {
                data: value,
                left: ptr::null(),
                right: ptr::null(),
                parent: ptr::null(),
            };
        }
    }
}

fn main() {
    println!("Hello, world!");
}
  

И вот ошибка:

 error[E0412]: type name `T` is undefined or not in scope
  --> src/main.rs:14:15
   |
14 |     impl Tree<T> {
   |               ^ undefined or not in scope
   |
   = help: no candidates by the name of `T` found in your project; maybe you misspelled the name or forgot to import an external crate?

error[E0412]: type name `T` is undefined or not in scope
  --> src/main.rs:15:30
   |
15 |         pub fn new() -> Tree<T> {
   |                              ^ undefined or not in scope
   |
   = help: no candidates by the name of `T` found in your project; maybe you misspelled the name or forgot to import an external crate?

error[E0412]: type name `T` is undefined or not in scope
  --> src/main.rs:19:37
   |
19 |         pub fn insert(amp;self, value: T) {
   |                                     ^ undefined or not in scope
   |
   = help: no candidates by the name of `T` found in your project; maybe you misspelled the name or forgot to import an external crate?

error[E0425]: unresolved name `root`. Did you mean `self.root`?
  --> src/main.rs:20:16
   |
20 |             if root.is_null() {
   |                ^^^^

error[E0106]: missing lifetime specifier
 --> src/main.rs:5:15
  |
5 |         left: amp;Node<T>,
  |               ^ expected lifetime parameter

error[E0106]: missing lifetime specifier
 --> src/main.rs:6:16
  |
6 |         right: amp;Node<T>,
  |                ^ expected lifetime parameter

error[E0106]: missing lifetime specifier
 --> src/main.rs:7:17
  |
7 |         parent: amp;Node<T>,
  |                 ^ expected lifetime parameter

error[E0106]: missing lifetime specifier
  --> src/main.rs:11:15
   |
11 |         root: amp;Node<T>,
   |               ^ expected lifetime parameter
  

Я действительно не понимаю, что в этом плохого. Я действительно не понимаю, как работают указатели Rust.

Ответ №1:

В этом случае у вас основная синтаксическая ошибка, она должна быть

 impl<T> Tree<T>
  

Оттуда вы увидите, что вам нужно if self.root.is_null() .

Затем для структуры данных требуются спецификаторы времени жизни, поскольку вы используете ссылки. Использование наиболее простого из этого синтаксиса в конечном итоге приводит к

 error[E0309]: the parameter type `T` may not live long enough
  

Итак, вы используете T: 'a там … и в итоге получаете:

 use std::ptr;

struct Node<'a, T: 'a> {
    data: T,
    left: amp;'a Node<'a, T>,
    right: amp;'a Node<'a, T>,
    parent: amp;'a Node<'a, T>,
}

struct Tree<'a, T: 'a> {
    root: amp;'a Node<'a, T>,
}

impl<'a, T> Tree<'a, T> {
    pub fn new() -> Tree<'a, T> {
        Tree { root: ptr::null() }
    }

    pub fn insert(amp;self, value: T) {
        if self.root.is_null() {
            self.root = Node {
                data: value,
                left: ptr::null(),
                right: ptr::null(),
                parent: ptr::null(),
            };
        }
    }
}

fn main() {
    println!("Hello, world!");
}
  

Это выдает другую ошибку

 21 |             root: ptr::null(),
   |                   ^^^^^^^^^^^ expected reference, found *-ptr
  

Это потому, что ptr::null() возвращает необработанные указатели, но вы заявили, что ваша структура данных использует ссылки.

Хорошо, это все, что я собираюсь сделать. Давайте вернемся к вашему вопросу…

Я новичок в Rust, и для упражнения я создаю простое универсальное двоичное дерево.

Я бы посоветовал вам рассмотреть что-то иное, чем написание структуры данных. В Rust они не простые. Если вы все еще хотите использовать этот подход, могу ли я порекомендовать слишком много списков.

Комментарии:

1. Я нашел ответ (см. Сообщение ниже), но спасибо за вашу помощь

Ответ №2:

Наконец-то я нашел способ сделать это. Я использовал std::optional вместо std::ptr для структуры узла, и это работает как указатель C.

 struct Node<T> {
    id: u32,
    data: T,
    left: Option<Box<Node<T>>>,
    right: Option<Box<Node<T>>>,
    parent: Option<Box<Node<T>>>,
}

struct Tree<T> {
    root: Option<Node<T>>,
}

impl<T> Node<T> {
    pub fn new(value: Option<T>,
               left: Option<Box<Node<T>>>,
               right: Option<Box<Node<T>>>,
               parent: Option<Box<Node<T>>>)
               -> Node<T> {
        Node {
            data: value.unwrap(),
            left: left,
            right: right,
            parent: parent,
        }
    }
}

impl<T> Tree<T> {
    pub fn new() -> Tree<T> {
        Tree { root: None }
    }

    pub fn insert(amp;mut self, value: T) {
        match self.root {
            Some(ref n) => {
                println!("Root is not empty");
            }
            None => {
                println!("Root is empty");
                self.root = Some(Node::new(Some(value), None, None, None));
            }
        }
    }
}

fn main() {
    println!("Hello, world!");

    let mut tree: Tree<i32> = Tree::new();
    tree.insert(42);
}
  

Комментарии:

1. Box является указателем, которому принадлежит объект, на который он указывает. Узел, владеющий дочерними элементами, звучит неплохо, но узел не может владеть своим родительским элементом. Родительский указатель — это то, что делает вашу структуру данных графоподобной, он имеет ссылочный цикл, и ни Box, ни обычные ссылки Rust не могут справиться с этим. Относительно просто использовать необработанный указатель для родительского указателя.