首页 > 解决方案 > 嵌套结构:“借来的值不够长”

问题描述

我一直在努力创建一个Table可以解析为Val特征对象以及其他数据类型(Number作为示例实现)的结构,并且可以在 Rust Playground 中看到。我无法解决这个借用/终身问题,但我的想法是,Number两者Table都是Val对象。但是,任何将对象写入String缓冲区的尝试都会导致编译失败。

我在这里做错了什么?借用检查器以这种方式运行的基本原理是什么?

use std::fmt;
use std::fmt::Write;

struct Number(f64);

struct Table<'a> {
    rows: Vec<Row<'a>>
    // Will have column vector as a member
}

impl <'a>Table<'a> {
    fn new(rows: Vec<Vec<Box<dyn Val>>>) -> Table {
        let mut table = Table {
            rows: rows.into_iter().map(|r| {
                Row {
                    parent: std::ptr::null_mut(),
                    cells: r
                }
            }).collect()
        };

        let parent = &mut table as *mut Table;

        table.rows = table.rows.into_iter().map(|mut r| {
            r.parent = parent;
            r
        }).collect();

        table
    }
}

struct Row<'a> {
    parent: *mut Table<'a>,
    cells: Vec<Box<dyn Val<'a>>>
}

impl <'a>Row<'a> {
    fn to_str(&'a self, buf: &mut String) -> fmt::Result {
        let mut cell_iter = self.cells.iter().enumerate().peekable();
        let _parent = unsafe { self.parent.as_ref() }; // Real implementation will need parent ref
        while let Some((idx,_c)) = cell_iter.next() { // Real implementation will cycle through columns in parent struct
            match self.cells.get(idx) {
                Some(v) => v.to_str(buf),
                None => Ok(())
            }?;
            if let Some(_) = cell_iter.peek() {
                write!(buf, ",")?;
            }
        }
        Ok(())
    }
}

pub trait Val<'a> {
    fn to_str(&'a self, buf: &mut String) -> fmt::Result;
}

pub trait ObjWriter<'a> {
    fn to_str(&'a self, buf: &'a mut String) -> fmt::Result;
}

impl <'a>ObjWriter<'a> for dyn Val<'a> {
    fn to_str(&'a self, buf: &mut String) -> fmt::Result { self.to_str(buf) }
}

impl <'a>Val<'a> for Table<'a> {
    fn to_str(&'a self, buf: &mut String) -> fmt::Result {
        write!(buf, "(START TABLE:")?;
        let mut row_iter = self.rows.iter().peekable();
        while let Some(r) = row_iter.next() {
            r.to_str(buf)?;
            write!(buf, "\n")?;
        }
        write!(buf, "END TABLE)")
    }
}

impl Number {
    fn to_str(&self, buf: &mut String) -> fmt::Result {
        write!(buf,"{}",self.0)
    }
}

impl <'a>Val<'a> for Number {
    fn to_str(&self, buf: &mut String) -> fmt::Result {
        self.to_str(buf)
    }
}

fn main() {
    let table = Table::new(vec![
        vec![Box::new(Number(0.5)),Box::new(Table::new(Vec::new()))],
        vec![Box::new(Table::new(Vec::new())),Box::new(Number(0.5))],
    ]);

    let mut buf = String::new();
    table.to_str(&mut buf);
    println!("{}",buf)
}
error[E0597]: `table` does not live long enough
   --> src/main.rs:98:5
    |
92  |       let table = Table::new(vec![
    |  ____________________________-
93  | |         vec![Box::new(Number(0.5)),Box::new(Table::new(Vec::new()))],
94  | |         vec![Box::new(Table::new(Vec::new())),Box::new(Number(0.5))],
95  | |     ]);
    | |_____- cast requires that `table` is borrowed for `'static`
...
98  |       table.to_str(&mut buf);
    |       ^^^^^ borrowed value does not live long enough
99  |       println!("{}",buf)
100 |   }
    |   - `table` dropped here while still borrowed

标签: rustlifetimeborrow-checkerborrowing

解决方案


我按照trentcl的建议做了,注意到我的指针在移动时会失效,我重构了我的代码,以便任何Row方法都将父表的引用作为第一个参数。然后我删除了所有<'a>后续的编译器错误,直到它起作用。

所以整个问题是允许生命周期失控并使用不安全的方法来获取父引用。去搞清楚...


推荐阅读