本指南將說明
fuchsia_inspect_derive敬上
假設您已熟悉
檢查
並具備
fuchsia_inspect 程式庫。
總覽
fuchsia_inspect_derive 程式庫提供人體工學巨集、特徵和
fuchsia_inspect 程式庫周圍的智慧指標,可讓您
透過以下方式,將檢查與 Rust 程式碼集整合:
- 擁有來源資料及檢查 RAII 類型相同的資料
- 保持慣用的態度。針對基元、通用室內配備的頂級支援 可變動模式和非同步模式
- 產生重複的樣板程式碼
- 提供統一附加類型以檢查的方式
- 能逐步整合現有程式碼集,包括需要
「尚未」支援檢查功能,且與
fuchsia_inspect。
- 支援缺乏檢查整合的外國類型。詳情請見
IDebug<T>可查看使用方式和限制條件。
同時保留手動檢查的效能和語意 整合方式:
- 執行精細的檢查樹狀結構修改,其中邏輯分葉節點是 且會獨立更新
- 僅套用靜態分派,以免產生額外的執行階段負擔。
- 不使用任何其他同步基元。
注意事項
將 Rust 程式碼集與這個程式庫整合時,請注意下列事項:
- 程式庫會反映 Rust 程式的內部類型階層。限定開放
結構上的修改,例如重新命名、整併及省略欄位
支援 (類似 Serde)。如果需要檢查樹狀結構
與類型階層截然不同
直接使用 fuchsia_inspect。
- 目前尚未支援部分功能,你必須採取以下行動:
手動實作 Inspect:- Lazy 節點、直方圖和檢查陣列。
- Option<T>及其他列舉。
- 集合類型,例如向量和地圖。
- StringReferences
 
- 程式庫會宣傳自訂智慧指標,用於建立另一個 資料包裝方式
快速入門
本節舉例說明您如何運用現有資料結構 然後將檢查套用至該結構讓我們從簡單的範例「Yak」開始:
struct Yak {
    // TODO: Overflow risk at high altitudes?
    hair_length: u16,       // Current hair length in mm
    credit_card_no: String, // Super secret
}
impl Yak {
    pub fn new() -> Self {
        Self { hair_length: 5, credit_card_no: "<secret>".to_string() }
    }
    pub fn shave(&mut self) {
        self.hair_length = 0;
    }
}
接著,請思考這個建築工地:
let mut yak = Yak::new();
yak.shave();
現在我們讓這隻獨木舟可供檢查吧!請特別注意以下幾點:
- 顯示目前的髮長
- 顯示八爪族的刮鬍次數
- 信用卡號碼「不得」公開
現在,請使用 fuchsia_inspect_derive 將這個 Yak 設為可檢查狀態:
use fuchsia_inspect_derive::{
    IValue,      // A RAII smart pointer that can be attached to inspect
    Inspect,     // The core trait and derive-macro
    WithInspect, // Provides `.with_inspect(..)`
};
#[derive(Inspect)]
struct Yak {
    #[inspect(rename = "hair_length_mm")] // Clarify that it's millimeters
    hair_length: IValue<u16>, // Encapsulate primitive in IValue
    #[inspect(skip)] // Credit card number should NOT be exposed
    credit_card_no: String,
    shaved_counter: fuchsia_inspect::UintProperty, // Write-only counter
    inspect_node: fuchsia_inspect::Node,           // Inspect node of this Yak, optional
}
impl Yak {
    pub fn new() -> Self {
        Self {
            hair_length: IValue::new(5), // Or if you prefer, `5.into()`
            credit_card_no: "<secret>".to_string(),
            // Inspect nodes and properties should be default-initialized
            shaved_counter: fuchsia_inspect::UintProperty::default(),
            inspect_node: fuchsia_inspect::Node::default(),
        }
    }
    pub fn shave(&mut self) {
        self.hair_length.iset(0); // Set the source value AND update the inspect property
        self.shaved_counter.add(1u64); // Increment counter
    }
}
現在,在主要程式 (或單元測試中) 建構泛舟, 新增至檢查樹狀結構:
// Initialization
let mut yak = Yak::new()
    .with_inspect(/* parent node */ inspector.root(), /* name */ "my_yak")?;
assert_data_tree!(inspector, root: {
    my_yak: { hair_length_mm: 5u64, shaved_counter: 0u64 }
});
// Mutation
yak.shave();
assert_data_tree!(inspector, root: {
    my_yak: { hair_length_mm: 0u64, shaved_counter: 1u64 }
});
// Destruction
std::mem::drop(yak);
assert_data_tree!(inspector, root: {});
現在,您已經整合一個簡易程式與「檢查」功能。本指南的其餘部分 說明瞭這個程式庫的類型、特徵和巨集,以及如何套用於 和現實世界的計畫
導引 Inspect
derive(Inspect) 可以新增至任何已命名的結構體,但其每個欄位皆可
也必須實作 Inspect (inspect_node 和略過的欄位除外)。
程式庫為幾種類型提供 Inspect 的實作:
- IOwned智慧指標
- 許多常見的內部可變動性包裝函式
- 所有檢查屬性 (UintProperty、StringProperty等),但以下項目除外: 陣列和直方圖
- 其他 Inspect類型。請參閱建立巢狀結構一節。
如果您新增的類型不是 Inspect,就會發生編譯器錯誤:
#[derive(Inspect)]
struct Yakling {
    name: String, // Forgot to wrap, should be `name: IValue<String>`
}
// error[E0599]: no method named `iattach` found for struct
// `std::string::String` in the current scope
巢狀 Inspect 類型
Inspect 類型可自由建立巢狀結構,如下所示:
// Stable is represented as a node with two child nodes `yak` and `horse`
#[derive(Inspect)]
struct Stable {
    yak: Yak,     // Yak derives Inspect
    horse: Horse, // Horse implements Inspect manually
    inspect_node: fuchsia_inspect::Node,
}
欄位和屬性
除了略過的欄位和 inspect_node 以外,所有欄位都必須實作
Inspect,針對 &mut T 或 &T。
如果有 inspect_node 欄位,執行個體會在
檢查樹狀結構必須是 fuchsia_inspect::Node:
#[derive(Inspect)]
struct Yak {
    name: IValue<String>,
    age: IValue<u16>,
    inspect_node: fuchsia_inspect::Node, // NOTE: Node is present
}
// Yak is represented as a node with `name` and `age` properties.
如果沒有提供 inspect_node,欄位會直接附加至父項節點
(這表示系統會忽略你提供給 with_inspect 的名稱):
#[derive(Inspect)]
struct YakName {
    title: IValue<String>, // E.g. "Lil"
    full_name: IValue<String>, // E.g. "Sebastian"
                           // NOTE: Node is absent
}
// YakName has no separate node. Instead, the `title` and `full_name`
// properties are attached directly to the parent node.
如果您的類型需要動態新增或移除節點或屬性 而該容器應擁有檢查節點需要檢查節點 將會在初始連結後,來新增或移除節點或屬性。
derive(Inspect) 支援下列欄位屬性:
- inspect(skip):檢查功能會忽略這個欄位。
- inspect(rename = "foo"):請改用其他名稱。根據預設,欄位名稱
- inspect(forward):將連結轉送至內部- Inspect類型,並省略這類類型 建立巢狀結構的巢狀結構層級所有其他欄位都不應接受檢查 屬性。類型不得包含- inspect_node欄位。適用於包裝函式類型。 例如:
#[derive(Inspect)]
struct Wrapper {
    // key is not included, because inspect has been forwarded.
    _key: String,
    #[inspect(forward)]
    inner: RefCell<Inner>,
}
#[derive(Inspect)]
struct Inner {
    name: IValue<String>,
    age: IValue<u16>,
    inspect_node: fuchsia_inspect::Node,
}
// Wrapper is represented as a node with `name` and `age` properties.
手動代管檢查類型
如要整合直接使用 fuchsia_inspect 的程式碼集,
型別不知道 fuchsia_inspect_derive。不要新增
手動管理類型做為欄位,直接設為 Inspect 類型。
針對類型手動實作 Inspect。
請避免在 Inspect 特徵之外手動附加檔案,
因為 fuchsia_inspect_derive 中的附件在建構後發生。
在建構函式中附加檔案,可能會導致其檢查狀態在沒有通知的情況下
不存在。
附加至檢查樹狀結構
檢查類型應附加一次,且在建立例項後立即附加。
方法是使用 with_inspect 擴充功能 trait 方法:
let yak = Yak::new().with_inspect(inspector.root(), "my_yak")?;
assert_data_tree!(inspector, root: { my_yak: { name: "Lil Sebastian", age: 3u64 }});
如果您有巢狀 Inspect 結構,只需附加頂層
類型。系統會以隱含方式附加以下巢狀類型:
// Stable owns a Yak, which also implements Inspect.
let stable = Stable::new().with_inspect(inspector.root(), "stable")?;
assert_data_tree!(inspector,
    root: { stable: { yak: { name: "Lil Sebastian", age: 3u64 }}});
請注意,如果是從 Stable 內建構 Yak,就不會
with_inspect 通話正在分享螢幕畫面。反之,Yak 會自動以 YAML 格式附加
Stable 的子項。不過,您仍可附加 Yak
頂層類型,例如 Yak 的單元測試。您可以藉此測試
隔離 Inspect 類型。
您可以選擇在建構函式中提供檢查節點,而非
在建築工地明確呼叫 with_inspect。首先,請確認
類型「不」位於其他 Inspect 類型之下 (這會導致
重複附件)。請大家務必清楚地記錄這個事實
讓發出呼叫的使用者瞭解您的附件慣例。
內部可變動性
在 Rust (尤其是 async Rust) 中,通常需要使用內部
可變動性這個程式庫提供適用於多種智慧功能的 Inspect 實作
指標和鎖定:
- std:- Box、- Arc、- Rc、- RefCell、- Mutex和- RwLock- 請注意,Cell無法運作。請改為升級至RefCell。
 
- 請注意,
- parking_lot:- Mutex和- RwLock
- futures:- Mutex
一般來說,derive(Inspect) 類型中的內部可變動性有效:
#[derive(Inspect)]
struct Stable {
-   yak: Yak,
+   yak: Arc<Mutex<Yak>>,
-   horse: Horse,
+   horse: RefCell<Horse>,
    inspect_node: fuchsia_inspect::Node,
}
請務必將智慧指標放入可變動性包裝函式中:
struct Yak {
-   coins: IValue<Rc<RwLock<u32>>>,  // Won't compile
+   coins: Rc<RwLock<IValue<u32>>>,  // Correct
}
如果內部類型位於鎖定後,如果鎖定,連結就會失敗 由其他人取得因此,請一律附加在 建立例項。
手動導入 Inspect
derive(Inspect) 衍生巨集會產生
impl Inspect for &mut T { .. }。這通常沒有問題
在某些情況下,您可能需要手動實作 Inspect。幸運的是
Inspect 特徵非常簡單:
trait Inspect {
    /// Attach self to the inspect tree
    fn iattach(self, parent: &Node, name: AsRef<str>) -> Result<(), AttachError>;
}
請勿針對資料中的結構錯誤傳回 AttachError。
請改用記錄檔或檢查節點回報錯誤。
AttachError 會保留給
將會失敗整個連結。
IOwned 智慧指標
智慧指標聽起來很可怕,但您可能每天都會用到。適用對象
例如,Arc 和 Box 是智慧指標。是以靜態方式調度 Pod
並在 Rust 中取得一流支援 (透過 deref 強制轉換)。如此一來
以降低幹擾程度
fuchsia_inspect_derive 提供幾個實用的智慧指標,
Inspect,可用於包裝基元、可偵錯類型等。他們
所有的行為都相同:IOwned<T> 智慧指標擁有一般
「來源類型」T 和部分相關聯的檢查資料。
以下是 IOwned API 的示範:
let mut number = IValue::new(1337u16) // IValue is an IOwned smart pointer
    .with_inspect(inspector.root(), "my_number")?; // Attach to inspect tree
// Dereference the value behind the IValue, without mutating
assert_eq!(*number, 1337u16);
{
    // Mutate value behind an IOwned smart pointer, using a scope guard
    let mut number_guard = number.as_mut();
    *number_guard = 1338;
    *number_guard += 1;
    // Inspect state not yet updated
    assert_data_tree!(inspector, root: { my_number: 1337u64 });
}
// When the guard goes out of scope, the inspect state is updated
assert_data_tree!(inspector, root: { my_number: 1339u64 });
number.iset(1340); // Sets the source value AND updates inspect
assert_data_tree!(inspector, root: { my_number: 1340u64 });
let inner = number.into_inner(); // Detaches from inspect tree...
assert_eq!(inner, 1340u16); // ...and returns the inner value.
IOwned<T> 智慧指標不應直接例項化,而應改為
其變體:
IValue<T>
IValue<T> 智慧指標會納入基本 (或任何類型 T: Unit)。
例如,IValue<f32> 會以 DoubleProperty 表示,
IValue<i16> 以 IntProperty 表示。
基本的 IValue 會產生與使用純色結構相同的結構
直接檢查屬性那麼,為什麼要使用 IValue?如果您只有
需要寫入或遞增值,您可以使用純檢查屬性。如果發生以下情況:
您也需要讀取值,應使用 IValue。
IDebug<T>
IDebug<T> 智慧指標會納入可進行偵錯的類型,並維護偵錯作業
T 表示為 StringProperty。這適用於:
- 外國類型無法新增檢查實作
- 偵錯,以便快速驗證程式的某些狀態
避免在正式版程式碼中使用偵錯表示法,因為這類表示法會 下列問題:
- 每次檢查更新都會寫入偵錯表示法,因此 不必要的效能負擔
- 偵錯表示法可能會耗盡檢查 VMO 的空間 整個檢查狀態的截斷。
- 偵錯表示法無法與隱私權管道整合: 個人識別資訊會做為偵錯字串的一部分公開,因此整個欄位都必須 視為個人識別資訊管理自己的結構化資料可讓您精細遮蓋資料 包含個人識別資訊的欄位
Unit 特徵
Unit 特徵用於說明類型的檢查表示法、如何
以及如何更新容器請根據行動廣告代碼的類型,加以實作
做為邏輯分葉節點,但「不支援」個別欄位的更新。這個圖書館
提供適用於大多數基元的 Unit 實作。例如 u8
u16、u32 和 u64 會以 UintProperty 表示。
IValue 中的用量
Unit 類型應納入 IValue<T: Unit> (請見上方說明) 中,以便使用 RAII
的可檢查類型不建議在 Unit 上呼叫方法
導引 Unit
有時邏輯 Unit 是複合類型。您可以為已命名的
結構體,只要其欄位也實作 Unit 即可。例如:
// Represented as a Node with two properties, `x` and `y`, of type UintProperty
#[derive(Unit)]
struct Point {
    x: f32,
    y: f32,
}
Unit 可以是巢狀結構,但請注意,所有欄位仍會寫入
兩者保持一致:
// Represented as a Node with two child nodes `top_left` and `bottom_right`
#[derive(Unit)]
struct Rect {
    #[inspect(rename = "top_left")]
    tl: Point,
    #[inspect(rename = "bottom_right")]
    br: Point,
}
屬性
derive(Unit) 支援下列欄位屬性:
- inspect(skip):檢查功能會忽略這個欄位。
- inspect(rename = "foo"):請改用其他名稱。根據預設,欄位名稱