鍵.png)
ASP.NET Web API快速入門介紹
理想情況下,這些方法不應(yīng)該列出,除非密碼管理器已解鎖。
當(dāng)前解決方案的問題是它使用運(yùn)行時(shí)檢查,所以在調(diào)用list_passwords方法時(shí)不會(huì)給我們?nèi)魏尉幾g時(shí)錯(cuò)誤,我們只會(huì)在運(yùn)行時(shí)注意到問題以解決這些問題。下面讓我們探索一個(gè)編譯時(shí)檢查的解決方案。
該方案不是只使用一個(gè)密碼管理器結(jié)構(gòu)體,我們使用兩個(gè)結(jié)構(gòu)體,一個(gè)鎖定的密碼管理器結(jié)構(gòu)體和一個(gè)解鎖的密碼管理器結(jié)構(gòu)體:
struct LockedPasswordManager {
master_pass: String,
passwords: HashMap<String, String>,
}
struct UnlockedPasswordManager {
master_pass: String,
passwords: HashMap<String, String>,
}
那么我們就可以定義兩個(gè)單獨(dú)的實(shí)現(xiàn)塊:
impl LockedPasswordManager {
pub fn new(master_pass: String) -> Self {
LockedPasswordManager {
master_pass,
passwords: Default::default(),
}
}
// 解鎖密碼管理器
pub fn unlock(&self, master_pass: String) -> UnlockedPasswordManager {
UnlockedPasswordManager {
master_pass: self.master_pass.clone(),
passwords: self.passwords.clone(),
}
}
// 獲取密碼管理器的加密算法
pub fn encryption(&self) -> String {
todo!()
}
// 獲取密碼管理器的版本信息
pub fn version(&self) -> String {
todo!()
}
}
在LockedPasswordManager的實(shí)現(xiàn)塊中,包含一個(gè)構(gòu)造函數(shù),一個(gè)返回UnlockedPasswordManager結(jié)構(gòu)體的unlock方法,它還包含encryption方法和version方法。同時(shí),我們將lock方法、list_passwords方法和add_password方法移動(dòng)到了UnlockedPasswordManager實(shí)現(xiàn)塊中:
impl UnlockedPasswordManager {
// 對(duì)密碼管理器加鎖
pub fn lock(&self) -> LockedPasswordManager {
LockedPasswordManager {
master_pass: self.master_pass.clone(),
passwords: self.passwords.clone()
}
}
// 列出所有密碼
pub fn list_passwords(&self) -> &HashMap<String, String> {
todo!()
}
// 向密碼管理器添加密碼
pub fn add_password(&mut self, name: String, password: String) {
todo!()
}
// 獲取密碼管理器的加密算法
pub fn encryption(&self) -> String {
todo!()
}
// 獲取密碼管理器的版本信息
pub fn version(&self) -> String {
todo!()
}
}
lock方法返回LockedPasswordManager結(jié)構(gòu)體,UnlockedPasswordManager還必須實(shí)現(xiàn)encryption和version方法,因?yàn)檫@些方法是通用的。
現(xiàn)在我們的API已經(jīng)更新了,讓我們看看如何在Main中使用它:
首先,我們修改PasswordManager為L(zhǎng)ockedPasswordManager,這將自動(dòng)給出編譯時(shí)錯(cuò)誤,因?yàn)閘ist_passwords方法和lock方法在LockedPasswordManager上不可用。這很好,因?yàn)橛脩糁荒茉L問在鎖定狀態(tài)下有意義的方法。修改main函數(shù)的代碼,如下:
fn main() {
let mut manager = LockedPasswordManager::new("password123".to_owned());
let manager = manager.unlock("password123".to_owned());
manager.list_passwords();
manager.lock();
}
很好,現(xiàn)在我們可以防止API的用戶在編譯時(shí)誤用它了。但由于幾個(gè)原因,這種解決方案仍然不理想。
注意,在這兩個(gè)結(jié)構(gòu)體中有相當(dāng)多的重復(fù)代碼,包含相同的字段。這兩個(gè)結(jié)構(gòu)體也必須實(shí)現(xiàn)兩個(gè)狀態(tài)之間共同的功能,encryption方法和version方法。
我們希望保留編譯時(shí)檢查,但不需要所有這些重復(fù)的代碼。下面我們使用泛型和零大小類型來實(shí)現(xiàn)這一點(diǎn)。
我們重新定義結(jié)構(gòu)體:
use std::{collections::HashMap, marker::PhantomData};
struct Locked;
struct Unlocked;
struct PasswordManager<State = Locked> {
master_pass: String,
passwords: HashMap<String, String>,
state: PhantomData<State>,
}
首先,我們回到了使用一個(gè)名為PasswordManager的結(jié)構(gòu)體,在結(jié)構(gòu)體中添加了一個(gè)名為State的新字段,類型為PhantomData。
同時(shí)還創(chuàng)建了兩個(gè)單元結(jié)構(gòu)體Locked和Unlocked,來表示鎖定和解鎖狀態(tài)。
接下來,我們還向PasswordManager結(jié)構(gòu)體添加一個(gè)泛型參數(shù)State,并將其默認(rèn)值設(shè)置為L(zhǎng)ocked。
添加泛型形參會(huì)導(dǎo)致一個(gè)問題,我們必須在結(jié)構(gòu)體的某個(gè)地方使用泛型形參,問題是我們并不關(guān)心這個(gè)泛型參數(shù),我們只使用它來創(chuàng)建不同的類型。
這就是PhantomData的來源,PhantomData是一種零大小類型,只是用于標(biāo)記。在編譯時(shí),這個(gè)字段實(shí)際上會(huì)被優(yōu)化掉,這就是為什么PhantomData被稱為零大小類型,因?yàn)樗徽加每臻g。
我們?cè)趯?shí)例化PasswordManager時(shí),必須將這個(gè)泛型形參替換為一個(gè)具體類型,默認(rèn)為L(zhǎng)ocked結(jié)構(gòu)體,也可能是Unlocked結(jié)構(gòu)體。
這么做這是有益的,因?yàn)殒i定的密碼管理器不等于解鎖的密碼管理器,這是兩種不同的類型,這意味著我們可以在每種類型上實(shí)現(xiàn)不同的方法。現(xiàn)在我們已經(jīng)重新定義了PasswordManager,讓我們來修改實(shí)現(xiàn)塊:
impl PasswordManager<Locked> {
// 解鎖密碼管理器
pub fn unlock(&self, master_pass: String) -> PasswordManager<Unlocked> {
PasswordManager {
master_pass: self.master_pass.clone(),
passwords: self.passwords.clone(),
state: PhantomData::<Unlocked>,
}
}
}
impl PasswordManager<Unlocked> {
// 對(duì)密碼管理器加鎖
pub fn lock(&self) -> PasswordManager<Locked> {
PasswordManager {
master_pass: self.master_pass.clone(),
passwords: self.passwords.clone(),
state: PhantomData::<Locked>,
}
}
// 列出所有密碼
pub fn list_passwords(&self) -> &HashMap<String, String> {
&self.passwords
}
// 向密碼管理器添加密碼
pub fn add_password(&mut self, name: String, password: String) {
self.passwords.insert(name, password);
}
}
impl<State> PasswordManager<State> {
// 獲取密碼管理器的加密算法
pub fn encryption(&self) -> String {
todo!()
}
// 獲取密碼管理器的版本信息
pub fn version(&self) -> String {
todo!()
}
}
impl PasswordManager {
pub fn new(master_pass: String) -> Self {
PasswordManager {
master_pass,
passwords: Default::default(),
state: Default::default(),
}
}
}
我們已經(jīng)完成了密碼管理器的實(shí)現(xiàn),讓我們繼續(xù)修改Main函數(shù):
fn main() {
let manager = PasswordManager::new("password123".to_owned());
let manager = manager.unlock("password123".to_owned());
manager.list_passwords();
manager.lock();
}
恭喜,現(xiàn)在我們知道如何使用泛型、零大小類型及狀態(tài)模式在提高Rust api性能的同時(shí)還能防止API的使用者濫用API。
文章轉(zhuǎn)自微信公眾號(hào)@coding到燈火闌珊
ASP.NET Web API快速入門介紹
2024年在線市場(chǎng)平臺(tái)的11大最佳支付解決方案
完整指南:如何在應(yīng)用程序中集成和使用ChatGPT API
選擇AI API的指南:ChatGPT、Gemini或Claude,哪一個(gè)最適合你?
用ASP.NET Core 2.1 建立規(guī)范的 REST API — 緩存和并發(fā)
企業(yè)工商數(shù)據(jù)API用哪種?
2024年創(chuàng)建社交媒體帖子的最佳圖像工具API
2024年小型企業(yè)的7個(gè)最佳短信應(yīng)用API
用gin寫簡(jiǎn)單的crud后端API接口
對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力
一鍵對(duì)比試用API 限時(shí)免費(fèi)