You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fnbroken_response_1(r:HttpResponse){
r.header("X-Unexpected","Spanish-Inquisition")// error: forgot to call status_line}fnbroken_response_2(r:HttpResponse){
r.status_line(200,"OK").body("Hello!").header("X-Unexpected","Spanish-Inquisition")// error: tried to send a header after body}
每个操作都会消耗自身并产生处于某种状态的新对象,或者(在 body 的情况下)不产生任何东西,结束该过程。
不能在状态行之前发送标头,因为该操作根本没有定义。
不能在 header之后发送状态行,因为同样地,在类型上也没有定义它。
无法在发送 body 后执行任何操作,因为对象已经被吞掉了。
第二次尝试
第一次尝试问题是是什么?
self 在方法里面被吞掉,如果 struct 很大,那么开销会比较大,
上面的设计的问题,意味着如果有多个 header 要发送,必须这样写:
也就是需要 r = r.header(h.key, h.value),这种写法比较丑
structHttpResponse(Box<ActualResponseState>);structHttpResponseAfterStatus(Box<ActualResponseState>);structActualResponseState{ ... }fnmany_headers(r:HttpResponse,headers:Vec<Header>){letmut r = r.status_line(200,"OK");for h in headers {// Having to do this is kind of annoying:
r = r.header(h.key, h.value);// Fortunately, if you forget the `r =` part,// the compile will fail.}
r.body("hello!")}
改变函数签名
// Let's try this again:implHttpResponseAfterStatus{fnheader(&mutself,key:&str,value:&str){// ...}}fnmany_headers(r:HttpResponse,headers:Vec<Header>){letmut r = r.status_line(200,"OK");for h in headers {// Ahhhh, much better.
r.header(h.key, h.value);}
r.body("hello!")}
// S is the state parameter. We require it to impl// our ResponseState trait (below) to prevent users// from trying weird types like HttpResponse<u8>.structHttpResponse<S:ResponseState>{// This is the same field as in the previous example.state:Box<ActualResponseState>,// This reassures the compiler that the parameter// gets used.marker: std::marker::PhantomData<S>,}// State type options.enumStart{}// expecting status lineenumHeaders{}// expecting headers or bodytraitResponseState{}implResponseStateforStart{}implResponseStateforHeaders{}
希望 HttpResponse<S> 类型仅与 S 的两个值一起使用: HttpResponse<Start> 和 HttpResponse<Headers> 。
/// Operations that are valid only in Start state.implHttpResponse<Start>{fnnew() -> Self{// ...}fnstatus_line(self,code:u8,message:&str)
-> HttpResponse<Headers>{// ...}}/// Operations that are valid only in Headers state.implHttpResponse<Headers>{fnheader(&mutself,key:&str,value:&str){// ...}fnbody(self,contents:&str){// ...}}
// Similar to before:structHttpResponse<S:ResponseState>{// This is the same field as in the previous example.state:Box<ActualResponseState>,// Instead of PhantomData<S>, we store an actual copy.extra:S,}// State type options. These now can add fields to HttpResponse.// Start adds no fields.structStart;// Headers adds a field recording the response code we sent.structHeaders{response_code:u8,}traitResponseState{}implResponseStateforStart{}implResponseStateforHeaders{}
implHttpResponse<Start>{fnstatus_line(self,response_code:u8,message:&str)
-> HttpResponse<Headers>{// Capture the response code in the new state.// In an actual HTTP implementation you'd// probably also want to send some data. ;-)HttpResponse{state:self.state,extra:Headers{
response_code,},}}}implHttpResponse<Headers>{fnresponse_code(&self) -> u8{// Hey look, it's the response codeself.extra.response_code}}
在 Headers 状态下,保证拥有 response_code ,并且能直接访问它。
#type/rust #public
The text was updated successfully, but these errors were encountered:
参考资料
原因
例子
简单的尝试
几个错误的返回:
第一次尝试
第二次尝试
第三次尝试
将状态建模为单个通用结构的类型参数,而不是为每个状态使用单独的结构。与完全独立的类型相比,这通常更少样板化并且更强大
缺点代码更难读
希望
HttpResponse<S>
类型仅与S
的两个值一起使用:HttpResponse<Start>
和HttpResponse<Headers>
。从技术上讲,如所写,用户能为自定义类型实现
ResponseState
并尝试将其与HttpResponse
一起使用。感到困扰,能使用**【sealed trait pattern】**来修复它。
State
和Headers
是仅作为【类型而不作为值存在的类】,使用zero-sized 枚举模式来确保这一点。像这样的类型被广泛称为【幻影类型】,这也是
std::marker::PhantomData
得名的地方。有点像第一个版本,
HttpResponse
上的所有这些操作都显示在为HttpResponse
生成的同一个rustdoc
上,但在不同的标题下,每个 impl 块一个。将文档注释附加到每个 impl 块,如上所示,以帮助用户跟进。
第一个版本每个状态一个类型的示例中,这些方法分布在多个页面上,使它们更难理解
要添加在任何状态下都有效的操作,只需让
S
不受约束:第四次尝试
S
,继承了它的内容Option<u8>
,它从None
开始,然后在status_line
中设置为Some(code)
,但这并不理想,None
,即使知道该字段是在状态行发送后设置的。Start
状态下,响应比Headers
小一个字节;这在这里可能微不足道,但如果要存储更多状态,它就会变得有用。Headers
状态下,保证拥有response_code
,并且能直接访问它。#type/rust #public
The text was updated successfully, but these errors were encountered: