Strange borrow compilation error

I am learning the Rust programming language and encountered a strange compilation error.

  • the corret code is:
use std::collections::HashMap;  
use std::fmt::Display;  
  
struct CellConfig<'a> {  
    name: &'a str,  
    nullable: bool,  
}  
  
struct SheetConfig<'a> {  
    sheet_name: &'a str,  
    cells: HashMap<&'a str, &'a CellConfig<'a>>,  
}  
  
impl<'a> SheetConfig<'a> {  
    fn new(sheet_name: &str) -> SheetConfig {  
        SheetConfig {  
            sheet_name,  
            cells: HashMap::new(),  
        }  
    }  
  
    fn get_cell_config(&self, cell_name: &str) -> Option<&CellConfig> {  
        self.cells.get(cell_name).copied()  
    }  
  
    fn add_cell_config(&mut self, cell_config: &'a CellConfig<'a>) {  
        self.cells.insert(cell_config.name, &cell_config);  
    }  
}  
  
fn main() {  
    // 创建配置信息  
    let mut sheet_config = SheetConfig::new("FeedData");  
    sheet_config.add_cell_config(&CellConfig { name: "feed_id", nullable: false });  
    sheet_config.add_cell_config(&CellConfig { name: "bal_today", nullable: false });  
  
  
    let cell_config = sheet_config.get_cell_config("feed_id");  
    println!("cell_config: {}", cell_config.unwrap().name);  
  
    let cell_config = sheet_config.get_cell_config("bal_today");  
    println!("cell_config: {}", cell_config.unwrap().name);  
}
  • then, I add new func to CellConfig
impl <'a> CellConfig<'a> {  
    fn new(name: &'a str, nullable: bool) -> Self {  
        Self { name, nullable}  
    }  
}
  • and changed the main code to use new:
fn main() {  
    // 创建配置信息  
    let mut sheet_config = SheetConfig::new("FeedData");  
    sheet_config.add_cell_config(&CellConfig::new("feed_id", false));  
    sheet_config.add_cell_config(&CellConfig::new("bal_today", false));  
  
    let cell_config = sheet_config.get_cell_config("feed_id");  
    println!("cell_config: {}", cell_config.unwrap().name);  
  
    let cell_config = sheet_config.get_cell_config("bal_today");  
    println!("cell_config: {}", cell_config.unwrap().name);  
}
  • and I got a compilation error !!!!
error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:38:35
   |
38 |     sheet_config.add_cell_config(&CellConfig::new("feed_id", false));
   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
   |                                   |
   |                                   creates a temporary value which is freed while still in use
39 |     sheet_config.add_cell_config(&CellConfig::new("bal_today", false));
   |     ------------------------------------------------------------------ borrow later used here
   |
help: consider using a `let` binding to create a longer lived value
   |
38 ~     let binding = CellConfig::new("feed_id", false);
39 ~     sheet_config.add_cell_config(&binding);
   |

Why is one of them unable to compile? Can someone explain this to me? Thanks

Rust has no language-level garbage collection that keeps values alive until all references to them are gone. Instead, references are used for short-lived borrows and values are dropped at the end of their drop scope (if not moved into something longer-lived or returned from the scope).

When you create a value when calling a function, the drop scope is the end of the statement.

sheet_config.add_cell_config(
    &                                   // (2) you take a reference to it
      CellConfig::new("feed_id", false) // (1) value is created
);   // (3) value drops at the end of the statement

The reference can't be used after that, which means sheet_config can't be used after that.

You can make it compile by following the advice,[1] however....


...taking a step back, you're overusing references. Use them for short-lived borrows, not long-lived data structures. If you're familiar with other languages, a Rust reference may not be the same thing as a [your other language] reference.


  1. cc1 and cc2 drop at the end of main now ↩︎

3 Likes

The main reason is static promotion : Rust Playground

// Your working code actually is
let mut sheet_config: SheetConfig<'static> = SheetConfig::new("FeedData");
const C: &'static CellConfig<'static> = &CellConfig { name: "feed_id", nullable: false };
sheet_config.add_cell_config(C);  

I.e the reference to type constructors &CellConfig { name: "feed_id", nullable: false } can be promoted to be 'static.

Update: aside from the lifetime abuse in OP, to learn the promoted/real static lifetime, you can make it work as follows ( Rust Playground )

impl <'a> CellConfig<'a> {  
    // step 1: const fn to generate a const value
    const fn new(name: &'a str, nullable: bool) -> Self {  
        Self { name, nullable}  
    }  
}

// step 2: explicit static lifetime instead of promoted static lifetime
const C: &CellConfig = &CellConfig::new("feed_id", false); 
sheet_config.add_cell_config(C); // works

And I see Chinese in your code, thus assume you read Chinese, then recommend my note-taking 静态生命周期提升 - rust-note

1 Like

I got it. Thank you very much.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.