![](/img/trans.png)
[英]Why can't I store a value and a reference to that value in the same struct?
[英]Why can I convert to another reference struct without error?
我寫了一個例子,運行時沒有編譯器錯誤
use std::collections::HashSet;
use std::error::Error;
use html5ever::rcdom;
use html5ever::rcdom::Handle;
use reqwest;
use soup::{prelude, QueryBuilder};
use soup::prelude::*;
use testquestion::testtrait::Test;
fn main() -> Result<(), Box<Error>> {
let resp = reqwest::get("https://docs.rs/soup/0.1.0/soup/")?;
let soup = Soup::from_reader(resp)?;
let result = soup
.tag("section")
.attr("id", "main")
.find()
.and_then(|section:Handle| -> Option<String> {
section
.tag("span")
.attr("class", "in-band")
.find()
.map(|span:Handle| -> String {
(&span as &rcdom::Node).text();
(&span as &Handle).text()
}
)
});
assert_eq!(result, Some("Crate soup".to_string()));
Ok(())
}
但我很困惑
(&span as &rcdom::Node).text();
(&span as &Handle).text()
trait NodeExt 有 text 方法,struct Node 和 Handle 實現了它。 但是為什么我可以在沒有編譯器錯誤的情況下將結構句柄的引用轉換為其他引用(句柄和節點)? 安全嗎? 我完全是 Rust 的新手。
pub trait NodeExt: Sized {
/// Retrieves the text value of this element, as well as it's child elements
fn text(&self) -> String {
let node = self.get_node();
let mut result = vec![];
extract_text(node, &mut result);
result.join("")
}
}
impl<'node> NodeExt for &'node rcdom::Node {
#[inline(always)]
fn get_node(&self) -> &rcdom::Node {
self
}
}
impl NodeExt for Handle {
#[inline(always)]
fn get_node(&self) -> &rcdom::Node {
&*self
}
}
編譯器通常不允許在unsafe
塊之外編寫任何不安全代碼,但編譯器和語言本身存在已知的健全性問題。 一些板條箱,特別是標准庫依賴於引擎蓋下的unsafe
實現,以提供安全的抽象。
你不是 100% 受到保護,就像在任何語言中一樣,但實際上你可以確定,如果安全 rust 程序編譯,它會在沒有未定義行為的情況下工作。
你的代碼是完全安全的,因為它編譯沒有錯誤。
這部分代碼試圖解決的核心問題是方法調用歧義。
(&span as &rcdom::Node).text();
(&span as &Handle).text()
考慮我是否將其更改為span.text()
。 編譯器應該調用什么方法? Handle::text
? rcdom::Node::text
? Rust 編譯器沒有規則來決定在這種特殊情況下調用什么。
我們有兩個選擇。 首先是使用完全限定的語法
rcdom::Node::text(&span);
Handle::text(&span)
實際上它是由rustc
建議的。
error[E0034]: multiple applicable items in scope
--> src/main.rs:20:5
|
20 | A::test();
| ^^^^^^^ multiple `test` found
|
note: candidate #1 is defined in an impl of the trait `Trait1` for the type `A`
--> src/main.rs:12:5
|
12 | fn test() {}
| ^^^^^^^^^
= help: to disambiguate the method call, write `Trait1::test(...)` instead
note: candidate #2 is defined in an impl of the trait `Trait2` for the type `A`
--> src/main.rs:16:5
|
16 | fn test() {}
| ^^^^^^^^^
= help: to disambiguate the method call, write `Trait2::test(...)` instead
其次是投射類型。 從&A
為&dyn TraitN
。 (更多關於dyn
用於 trait 對象的關鍵字)總是安全的是A
實現了 trait TraitN
。
Trait 對象沒有大小,這意味着編譯器無法理解它應該為特定的 trait 對象分配多少內存,因為它可以由多種類型實現,並且只存在於其實現者中。 實現 trait 的不同類型之間的大小可能會有所不同。
因此你不能直接施放A
。 即使編譯器知道在這種情況下 trait 對象將始終是A
,trait 對象在本質上是未確定大小的。
error[E0620]: cast to unsized type: `A` as `dyn Trait1`
--> src/main.rs:20:5
|
20 | (A as Trait1).test()
| ^^^^^^^^^^^^^
|
help: consider using a box or reference as appropriate
--> src/main.rs:20:6
|
20 | (A as Trait1).test()
| ^
您可以借用A
引用,這樣您將獲得固定大小的參考,並將其轉換為一個&dyn TraitN
,其大小也是固定的(因為它是參考)。 所以你得到類型為&dyn TraitN
對象,可以很容易地進行方法調用,沒有歧義。
let a: &dyn Trait1 = &A as &dyn Trait1;
a.test()
您實際上是在擦除A
類型,將其指向的內存視為特征。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.