使用`&str`在具有`new`方法的结构体中时,出现了Rust生命周期问题。

huangapple go评论72阅读模式
英文:

rust lifetime problem when using `&str` in struct with `new` method

问题

我正在尝试在我的结构体中使用&str类型。这个结构体有一个异步的new方法,所以我可以从环境文件中读取参数并创建一个实例。问题是我无法确定它的生命周期。

它一直告诉我cannot return value referencing local variable 'site_url' returns a value referencing data owned by the current function,我该如何解决这个问题呢?

pub struct AppContext<'a>{
    pub site_url: &'a str
}
impl<'a> AppContext<'a>{
    pub async fn new() -> AppContext<'a>{
        //根据环境变量填充网站URL
        let site_url = std::env::var("WEBSITE_URL").unwrap_or("http://localhost:3000".to_string());
        AppContext{
            site_url: &site_url
        }
    }
}

你需要使用生命周期参数来指定AppContext结构体中的site_url字段的生命周期。在new方法中,你可以使用&操作符来获取site_url的引用,并将其作为AppContext结构体的字段。

这样做的原因是,site_url是一个局部变量,它在new方法结束后将被销毁。通过使用引用,你可以确保site_url的生命周期与AppContext结构体的生命周期相匹配,从而避免了悬垂引用的问题。

英文:

I'm trying to use the &amp;str type in one of my struct.
this struct has an async new method so I can read the parameter from the env file and create an instance. the problem is I can't fix the lifetime of it.

pub struct AppContext&lt;&#39;a&gt;{
    pub site_url: &amp;&#39;a str
}
impl&lt;&#39;a&gt; AppContext&lt;&#39;a&gt;{
    pub async fn new()-&gt;AppContext&lt;&#39;a&gt;{
        //fill website url base on env variables
        let site_url =  std::env::var(&quot;WEBSITE_URL&quot;).unwrap_or(&quot;http://localhost:3000&quot;.to_string());
        AppContext{
            site_url: site_url.as_str()
        }
    }
}

it keep telling me cannot return value referencing local variable &#39;site_url&#39;
returns a value referencing data owned by the current function

how can I fix this?

答案1

得分: 1

如果允许这样做,将会导致使用后释放错误。请注意,site_url 的类型是 String。函数 String::as_str 接受一个 &'a String 并输出一个 &'a str。换句话说,对 str 的引用只在对底层 String 的引用有效的情况下才有效。

由于 site_url 是一个局部变量,它将在定义它的代码块结束时超出作用域。这意味着在那个时候使用的 &site_url(用于 site_url.as_str())只在那个时候有效。之后,字符串将被释放。

    pub async fn new() -> AppContext<'a> {
        // 根据环境变量填充网站 URL
        let site_url = std::env::var("WEBSITE_URL").unwrap_or("http://localhost:3000".to_string());
        AppContext {
            site_url: site_url.as_str(),
        }
        // site_url 是一个在这里超出作用域的局部变量。这是 site_url 将被丢弃的时候,对它的引用将不再有效的时候
    }

这意味着当你实际使用 new 函数最终生成的 AppContext 时,其中的 &'a str 将无效!

一个解决方案是重新定义 AppContext,使 site_url 成为一个 String,而不是 &str

英文:

If you were allowed to do this, it would result in a use-after-free error. Note that site_url has type String. The function String::as_str takes a &amp;&#39;a String and outputs a &amp;&#39;a str. In other words, the reference to the str is only valid for as long as the reference to the underlying String is.

Since site_url is a local variable, it will go out of scope at the end of the block where it's defined. This means that the &amp;site_url which is used in site_url.as_str() is only valid until that time. After that point, the string will be freed.

    pub async fn new()-&gt;AppContext&lt;&#39;a&gt;{
        //fill website url base on env variables
        let site_url =  std::env::var(&quot;WEBSITE_URL&quot;).unwrap_or(&quot;http://localhost:3000&quot;.to_string());
        AppContext{
            site_url: site_url.as_str()
        }
        // site_url is a local variable that goes out of scope here. This is 
        // when site_url will be dropped, and the reference to it will no longer be valid
    }

This means that by the time you're actually using the AppContext that the new function eventually produces, the &amp;&#39;a str inside it will be invalid!

One solution is to redefine AppContext so site_url is a String, not a &amp;str.

答案2

得分: 1

我相信这段代码对你有用。

你的AppContext仍然可以正常引用str,但它也支持像String这样拥有自己值的智能指针
这使得AppContext可以是str的所有者或借用者。你可以始终选择更方便的选项。

此外,你可以考虑使用Box<dyn Deref<str>>进行类型擦除,使AppContext不再是泛型的。

use std::ops::Deref;

#[derive(Debug)]
pub struct AppContext<D: Deref<Target = str>> {
    pub site_url: D,
}

impl<D: Deref<Target = str>> AppContext<D> {
    pub fn new(site_url: D) -> Self {
        Self { site_url }
    }
}

fn get_website_url() -> String {
    std::env::var("WEBSITE_URL").unwrap_or_else(|_| "http://localhost:3000".to_string())
}

fn main() {
    let owning_obj = AppContext::new(get_website_url());
    let dependant_obj = AppContext::new("https://Your/hardcoded/page.com");
    dbg!(owning_obj, dependant_obj);
}

希望对你有帮助!

英文:

I believe this code will work for you.

Your AppContext will still work for normal reference to str, but it will also support smart pointers like String which own their values.
This allows AppContext to be either owner or borrower of str. You will always choose the more convenient option.


    use std::ops::Deref;
    
    #[derive(Debug)]
    pub struct AppContext&lt;D: Deref&lt;Target = str&gt;&gt; {
        pub site_url: D,
    }
    
    impl&lt;D: Deref&lt;Target = str&gt;&gt; AppContext&lt;D&gt; {
        pub fn new(site_url: D) -&gt; Self {
            Self { site_url }
        }
    }
    
    fn get_website_url() -&gt; String {
        std::env::var(&quot;WEBSITE_URL&quot;).unwrap_or_else(|_| &quot;http://localhost:3000&quot;.to_string())
    }
    
    fn main() {
        let owning_obj = AppContext::new(get_website_url());
        let dependant_obj = AppContext::new(&quot;https://Your/hardcoded/page.com&quot;);
        dbg!(owning_obj, dependant_obj);
    }

Also, you may consider type erasure with Box&lt;dyn Deref&lt;str&gt;&gt; to make AppContext non-generic.

huangapple
  • 本文由 发表于 2023年7月27日 15:42:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/76777518.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定