英文:
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 &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<'a>{
pub site_url: &'a str
}
impl<'a> AppContext<'a>{
pub async fn new()->AppContext<'a>{
//fill website url base on env variables
let site_url = std::env::var("WEBSITE_URL").unwrap_or("http://localhost:3000".to_string());
AppContext{
site_url: site_url.as_str()
}
}
}
it keep telling me cannot return value referencing local variable 'site_url'
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 &'a String
and outputs a &'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 &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()->AppContext<'a>{
//fill website url base on env variables
let site_url = std::env::var("WEBSITE_URL").unwrap_or("http://localhost:3000".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 &'a str
inside it will be invalid!
One solution is to redefine AppContext
so site_url
is a String
, not a &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<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);
}
Also, you may consider type erasure with Box<dyn Deref<str>>
to make AppContext
non-generic.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论