微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如果分配,为什么会得到“借用时的临时值下降”,而通过函数传递时却没有呢?

如何解决如果分配,为什么会得到“借用时的临时值下降”,而通过函数传递时却没有呢?

我对Rust很新鲜。我主要有C和C ++的经验。

代码来自lol_html板条箱示例。

use lol_html::{element,HtmlRewriter,Settings};

let mut output = vec![];

{
    let mut rewriter = HtmlRewriter::try_new(
        Settings {
            element_content_handlers: vec![
                // Rewrite insecure hyperlinks
                element!("a[href]",|el| {
                    let href = el
                        .get_attribute("href")
                        .unwrap()
                        .replace("http:","https:");

                    el.set_attribute("href",&href).unwrap();

                    Ok(())
                })
            ],..Settings::default()
        },|c: &[u8]| output.extend_from_slice(c)
    ).unwrap();

    rewriter.write(b"<div><a href=").unwrap();
    rewriter.write(b"http://example.com>").unwrap();
    rewriter.write(b"</a></div>").unwrap();
    rewriter.end().unwrap();
}

assert_eq!(
    String::from_utf8(output).unwrap(),r#"<div><a href="https://example.com"></a></div>"#
);

但是如果我将element_content_handlers vec移到外面并进行分配,我会得到

借入时的临时价值下降

让行:

use lol_html::{element,Settings};

let mut output = vec![];

{
    let handlers = vec![
                // Rewrite insecure hyperlinks
                element!("a[href]",&href).unwrap();

                    Ok(())
                }) // this element is deemed temporary
            ];

    let mut rewriter = HtmlRewriter::try_new(
        Settings {
            element_content_handlers: handlers,r#"<div><a href="https://example.com"></a></div>"#
);

我认为该方法拥有向量的所有权,但我不明白为什么它不适用于简单分配。我不想先声明所有元素。我希望有一个简单的用法可以使它拥有所有元素。

编辑: 编译器建议在该行之前绑定该元素,但是如果我有很多元素怎么办?例如,我想避免命名50个元素。有没有一种方法可以不绑定所有元素?还要解释为什么临时生命周期在vec内部结束!在let绑定的情况下调用,但在我放置vec时不会调用!内部新建的结构传递给方法?最后一个问题对我来说很重要。

解决方法

当我第一次尝试重现您的问题时,我发现 try_new 不存在。它已在最新版本的 lol_html 中删除。将其替换为 new,您的问题没有重现。不过,我能够使用 v0.2.0 进行重现。由于问题与宏生成的代码有关,我尝试了 cargo expand(您需要安装的东西,see here)。

以下是 let handlers = ... 在 v0.2.0 中的扩展:

let handlers = <[_]>::into_vec(box [(
    &"a[href]".parse::<::lol_html::Selector>().unwrap(),::lol_html::ElementContentHandlers::default().element(|el| {
        let href = el.get_attribute("href").unwrap().replace("http:","https:");
        el.set_attribute("href",&href).unwrap();
        Ok(())
    }),)]);

这是它在 v0.3.0 中的扩展

let handlers = <[_]>::into_vec(box [(
    ::std::borrow::Cow::Owned("a[href]".parse::<::lol_html::Selector>().unwrap()),)]);

忽略第一行,宏vec是怎么回事!扩大。第二行显示了版本生成的差异。第一个借用解析的结果,第二个借用它的 Cow::Owned。 (Cow 代表写时复制,但它更普遍适用于您希望对某事物的借用或拥有版本通用的任何内容。

所以简短的回答是用于扩展到不拥有的东西的宏,现在它确实如此。至于为什么它在没有单独赋值的情况下工作,那是因为 Rust 自动为你创建了一个临时变量。

在大多数位置表达式上下文中使用值表达式时,会创建一个临时未命名的内存位置并初始化为该值,并且表达式计算为该位置,除非提升为静态

https://doc.rust-lang.org/reference/expressions.html#tempora..

最初 rust 为您创建了多个临时对象,它们都对相同的作用域有效,即调用 try_new 的作用域。当您将向量分解为自己的分配时,会为元素创建临时!仅对向量赋值的范围有效。

我查看了元素的 git blame! lol_html 中的宏,他们进行了更改,因为有人打开了一个本质上是您的问题的问题。所以我想说这是一个有漏洞的抽象中的错误,而不是你对 Rust 的理解问题。

,

您正在向量(2020-11-04 18:49:21.400909 [INFO] [2117345] [<ip>#APVH_www.<domain_name>.com:443] connection to [uds://tmp/lshttpd/APVH_www.<domain_name>.com:443:Django_Project-master_.sock] on request #0,confirmed,associated process: 0,running: 0,error: No such file or directory! 2020-11-04 18:49:21.400923 [INFO] [2117345] [<ip>#APVH_www.<domain_name>.com:443] connection to [uds://tmp/lshttpd/APVH_www.<domain_name>.com:443:Django_Project-master_.sock] on request #0,error: No such file or directory! 2020-11-04 18:49:21.400929 [NOTICE] [2117345] [<ip>#APVH_www.<domain_name>.com:443] Max retries has been reached,503! 2020-11-04 18:49:21.401018 [NOTICE] [2117345] [<ip>#APVH_www.<domain_name>.com:443] oops! 503 Service Unavailable 2020-11-04 18:49:21.401020 [NOTICE] [2117345] [<ip>#APVH_www.<domain_name>.com:443] Content len: 0,Request line: 'GET /djangotest/ HTTP/1.1' 2020-11-04 18:49:21.401021 [NOTICE] [2117345] [<ip>#APVH_www.<domain_name>.com:443] Redirect: #1,URL: / 2020-11-04 18:49:21.401038 [INFO] [2117345] [<ip>#APVH_www.<domain_name>.com:443] abort request...,code: 4 2020-11-04 18:49:28.166441 [INFO] [2117345] [<ip>#APVH_www.<domain_name>.com:443] File not found [/home/<user>/domains/<domain_name>.com/private_html/robots.txt] 2020-11-04 18:49:28.166473 [INFO] [2117345] [<ip>#APVH_www.<domain_name>.com:443] File not found [/home/<user>/domains/<domain_name>.com/private_html/404.shtml] ``` )内创建一个临时值。这意味着在向量内部创建的值仅存在于向量内部短暂的生命周期内。在向量声明的末尾,将释放该值,表示该值不再存在。这意味着在element内部创建的值仅在vec![]内部短暂存在的时间内存在。在vec![]的末尾,该值被释放,表示该值不再存在:

vec![]

然后尝试使用不存在的值创建一个let handlers = vec![ ______ | | element!("a[href]",|el| { | let href = el.get_attribute("href").unwrap().replace("http:",|"https:"); | el.set_attribute("href",&href).unwrap(); | Ok(()) | }),|______ ^ This value is temporary ]; > the element is freed here,it no longer exists!

HtmlRewriter

很明显,借用检查器可以解决此问题,并且您的代码无法编译。

这里的解决方案是使用Settings { element_content_handlers: handlers,// the element inside of `handlers` doesn't exist anymore! ..Settings::default() },将该元素绑定到变量:

let

然后创建矢量:

let element = element!("a[href]",|el| {
    let href = el.get_attribute("href").unwrap().replace("http:","https:");
    el.set_attribute("href",&href).unwrap();
    Ok(())
});

现在,该值已绑定到变量(let handlers = vec![element]; ),因此它的寿命足够长,可以稍后在element中借用

,

创建某些对象时,它会绑定到可能的最内部范围,以跟踪其生命周期。在较高作用域使用let绑定会将值绑定到该作用域,从而延长其寿命。如果要创建很多东西,然后对它们应用操作(例如,将它们传递给另一个函数),则通常有必要创建值向量,然后对它们应用转换。例如,

let xs = (0..10).map(|n| SomeStruct { n }).map(|s| another_function(s)).collect();

通过这种方式,您无需将SomeStruct对象明确绑定到任何对象。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。