首页 文章

借用值对于struct来说没有足够长的编译器错误

提问于
浏览
5

我是语言新手,仍在与借阅检查员作斗争 . 我已经看到一些库使用没有参数的new()函数a.k.a.构造函数并且它可以工作 . 基本上这意味着,返回的数据是在new的函数范围内创建的,并且在新范围的末尾不会被删除 .

当我自己尝试这个时,借用检查器不会让这个代码通过 . 除了将i32可变引用作为参数传递给构造函数之外,如何使其工作 .

我错过了什么吗?

#[derive(Debug)]
struct B<'a> {
    b: &'a i32
}

#[derive(Debug)]
struct A<'a> {
    one: B<'a>
}

impl<'a> A<'a> {
    fn new() -> A<'a> {
        // let mut b = 10i32;
        A {
            one: B{b: &mut 10i32}
        }
    }
}

fn main() {
    let a = A::new();
    println!("A -> {:?}", a);
}

编译错误 .

main.rs:15:19: 15:24 error: borrowed value does not live long enough
main.rs:15          one: B{b: &mut 10i32}
                                   ^~~~~
main.rs:12:20: 17:3 note: reference must be valid for the lifetime 'a as defined on the block at 12:19...
main.rs:12  fn new() -> A<'a> {
main.rs:13      // let mut b = 10i32;
main.rs:14      A {
main.rs:15          one: B{b: &mut 10i32}
main.rs:16      }
main.rs:17  }
main.rs:12:20: 17:3 note: ...but borrowed value is only valid for the block at 12:19
main.rs:12  fn new() -> A<'a> {
main.rs:13      // let mut b = 10i32;
main.rs:14      A {
main.rs:15          one: B{b: &mut 10i32}
main.rs:16      }
main.rs:17  }
error: aborting due to previous error

根据要求,这是我正在尝试使用的实际示例 . 有这个GUI库(Conrod),它有一些实例化它的步骤 . 就像下面的例子一样 .

let assets = find_folder::Search::ParentsThenKids(3, 3)
    .for_folder("assets").unwrap();
let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf");
let theme = Theme::default();
let glyph_cache = GlyphCache::new(&font_path).unwrap();
let ui = &mut Ui::new(glyph_cache, theme);

我的计划是将应用程序的绘图封装到结构中 . 那将有一个构造函数和一些辅助方法 . 为此,我必须有一个带有 conrod::Ui<GlyphCache<'a>> 类型实例的字段,这是上面的ui变量的类型 .

我认为向main添加东西(我的意思是在main中完成所有分配)可能不是最好的做事方式 .

let mut app_ui = app::AppUi::new(); // This would encapsulate all of the above configuration lines.

// use the ui here
for e in evets {
    app_ui.handle_input();
    app_ui.render();
}

AppUi的实施 . 它's not complete, but should show the general idea. Just to make sure we'在同一页面上,类型 conrod::Ui<GlyphCache<'a>> 需要一个生命周期参数 . 我想拥有与结构相同的生命周期 . 我知道如何做的唯一方法是让struct自己获取一个生命周期参数,并将其传递给UI类型 .

pub struct AppUi<'a> {
  pub ui: conrod::Ui<GlyphCache<'a>>,
  pub count: u16
}

impl<'a> AppUi<'a> {
  pub fn new() -> AppUi<'a> {
    let assets = find_folder::Search::ParentsThenKids(3, 3)
    .for_folder("assets").unwrap();

    let font_path = assets.join("FiraSans-Regular.ttf");
    let theme = Theme::default();
    let glyph_cache = GlyphCache::new(&font_path).unwrap();

    AppUi {
      ui: conrod::Ui::new(glyph_cache, theme),
      count: 0
    }
  }
}

=======================

我使用的解决方案,它最终工作(至少它现在有效) . 是创建一个辅助函数,它会返回一个glyph_cache并且只是使用它 . 我不确定它是否是惯用的Rust,现在就会使用它 . 应该习惯于使用借阅检查器 .

pub struct AppUi<'a> {
  pub ui: conrod::Ui<GlyphCache<'a>>,
  pub count: u16
}

impl<'a> AppUi<'a> {
  pub fn new() -> AppUi<'a> {

    AppUi {
      ui: conrod::Ui::new(GlyphCache::new(&get_default_font_path()).unwrap(), Theme::default()),
      count: 0
    }
  }
}

pub fn get_default_font_path() -> PathBuf {
  find_folder::Search::ParentsThenKids(3, 3)
    .for_folder("assets")
    .unwrap()
    .join("FiraSans-Regular.ttf")
}

2 回答

  • 2

    那是因为 &mut 10i32 在你的程序中活不了多久 . 您指定 one 将与 a 具有相同的生命周期,但 a 的寿命超过 one ,因为在 new 完成后i32超出范围 . 此代码将在另一方面起作用:

    #[derive(Debug)]
    struct B<'a> {
        b: &'a i32
    }
    
    #[derive(Debug)]
    struct A<'a> {
        one: B<'a>
    }
    
    impl<'a> A<'a> {
        fn new(x: &'a mut i32) -> A<'a> {
            // let mut b = 10i32;
            A {
                one: B{b: x}
            }
        }
    }
    
    fn main() {
        let mut x = 10i32;
        let a = A::new(&mut x);
        println!("A -> {:?}", a);
    }
    

    请注意 x 现在和 a 一样长,所以你的生命周期都很满意

  • 6

    理解这一点的关键是 & 引用代表借入,而不是拥有的值 . 生命周期注释不能控制值的存在时间;他们只是跟踪以确保借用的引用的引用比借用的引用本身更长,因此将它取消引用总是有效的 .

    借用引用可以引用堆栈(静态分配的内存)或堆(动态分配的内存)中的值 . 堆栈上的值具有相当明显的生命周期;从变量初始化时开始,直到块的末尾,这些值从堆栈中弹出 .

    堆上的值由堆栈中的指针所拥有;所以它们的生命周期由拥有它们的堆栈上的指针决定 . 但是,所有权可以在不同变量之间移动,因此如果将引用它们的指针的所有权从一个堆栈变量移动到另一个堆栈变量,则实际上可以使这些值具有更灵活的生命周期 .

    如果您编写具有如下签名的函数:

    fn new() -> A<'a> {}
    

    你所说的是你将返回 A ,其中包含的引用具有由调用者确定的生命周期 'a ;但是你可以在 new 函数中产生一个具有任意输入生命周期的值 .

    new 函数,您通常想要返回的是拥有值;或者可能带有基于某些输入参数的借位值,但您需要提供这些参考作为输入参数 .

    如果你更多地描述你想要做的事情,而不仅仅是提供一个玩具示例,它可能会有所帮助 . 你可以尝试在这里做几件事,但仅仅以玩具为例,很难确定要描述的内容 . 返回包含引用的对象的目的是什么?它是否可以对象进行堆分配,因此只需要在传递它时绕一个指针移动而不是复制整个值?在这种情况下,您可能需要 BoxVec . 它是否可以引用一些堆栈分配的变量?然后,您需要在包含的堆栈帧中分配它,并将具有该生存期的引用传递给您的函数,以便有一个具有适当生命周期的变量供其引用 . 如果您只是想返回一个包含整数的对象,那么您可以通过让对象直接包含整数来实现,而不是包含对它的引用 .

相关问题