Variant 变体类型
变体类型表示恰好来自给定枚举或标签之一的一个值。
type Vehicule = { #Car; #Moto; #Bicycle; #Plane; #Boat; };
Each tag can have it's own type. 每个标签都可以有自己的类型。
import Time "mo:base/Time"; actor { type Time = Time.Time; type Health = { #invicible; #alive : Nat; #dead : Time; }; }
You will usually combine variants, with the switch/case expression we've seen before.
您通常会将变体与我们之前见过的 switch/case 表达式组合起来。
import Time "mo:base/Time"; import Int "mo:base/Int"; import Nat "mo:base/Nat"; actor { type Time = Time.Time; public type Health = { #invicible; #alive : Nat; #dead : Time; }; public func medical_check(h : Health) : async Text { switch(h){ case(#invicible) { return("Woah I've never seen someone with s much energy !"); }; case(#alive(n)){ return("You seem to be in good shape, you have " # Nat.toText(n) # " energy points"); }; case(#dead(t)){ return("💀 since " # Int.toText(t)); }; }; }; }
You'll notice that one advantage of using variants, is that our switch/case doesn't need to cover the (_) case ! 您会注意到使用变体的一个优点是我们使用switch/case语句时不需要覆盖 (_) case!
Result type 返回值类型
The type Result is extremly useful if you want to propagate errors and indicate to other people/developers what went wrong.
如果您想要传递错误信息并向其他人/开发人员指示出了什么问题,那么 Result 类型非常有用。
The type Result is defined as :类型Result可定义为:
type Result<Ok, Err> = {#ok : Ok; #err : Err}
This means the type Result is just a variant with two tags #ok and #err. These two tags can be of type Ok and type Err.
One common type for Ok and Err is the following.
type Result<(), Text> = {#ok ; #err : Text};
In case everything went right we just return #ok without additional informations, but if we encounter an error we want to propagate a message in the #err.
如果程序运行一切顺利,只返回#ok,而不提供其它信息,但如果遇到错误,希望在#err 中传递一条文本错误消息。
import Result "mo:base/Result"; import Principal "mo:base/Principal"; actor { public type Result = Result.Result; public shared ({caller}) func register() : async Result<(), Text> { if(Principal.isAnonymous(caller)){ return #err("You need to be authenticated to register"). } else { // Do something return #ok; } }; };
You could also create a variant type Error and use it in the Result.
您还可以创建变体类型“错误”并在结果中使用它。
HTTP request HTTP请求
如果对HTTP相关知识还不熟悉的话,建议先学习一下HTTP相关知识。
Canisters 能够直接应答 http 请求!
您需要实现一个名为 http_request 的public方法,以允许您的canister容器应答任何 http 调用。
在名为 https://http.mo/ 的模块中,我们将声明以下类型。
module { public type HeaderField = (Text, Text); public type Request = { body : Blob; headers : [HeaderField]; method : Text; url : Text; }; public type Response = { body : Blob; headers : [HeaderField]; status_code : Nat16; streaming_strategy : ?StreamingStrategy; }; public type StreamingStrategy = { #Callback: { callback : StreamingCallback; token : StreamingCallbackToken; }; }; public type StreamingCallback = query (StreamingCallbackToken) -> async (StreamingCallbackResponse); public type StreamingCallbackToken = { content_encoding : Text; index : Nat; key : Text; }; public type StreamingCallbackResponse = { body : Blob; token : ?StreamingCallbackToken; }; };
在main.mo文件中
import HTTP "http"; import Text "mo:base/Text"; actor { public query func http_request(request : HTTP.Request) : async HTTP.Response { let response = { body = Text.encodeUtf8("Hello world"); headers = [("Content-Type", "text/html; charset=UTF-8")]; status_code = 200 : Nat16; streaming_strategy = null }; return(response) }; };
这就是我们为canister容器实现基本 http 响应的方式! 现在,如果您部署此canister容器并在浏览器中访问它,您应该会看到一个带有文本的空白页面!
Intercanister messages 💬 canister间的通信
One of the best thing on the IC is the ability for a canister to call another canister methods just with a few lines of code. 🤯
The first thing you migth want to do is declare the interface of the actor you're gonna interact with.
Let's imagine we have an actor defined somewhere else.
IC 上最好的事情之一是canister容器只需几行代码即可调用另一个canister容器方法的能力。
需要做的第一件事是声明您要与之交互的actor的界面。
假设我们在其它地方定义了一个actor。
actor Stranger { public shared ({caller}) func hello() : async Text { return("I was called by ) } public shared ({caller}) func another_function_that_does_something() : async () { //Do something return; }; }
We would define it's interface like this.
(If the actor have severals methods but you only want to call one you don't need to declare all other methods).
我们会这样定义它的接口。
(如果actor参与者有多个方法,但您只想调用一个方法,则无需声明所有其它方法)。
let other_canister : actor { hello : () -> async Text; } = actor("CANISTER_ID");
You'll notice that you need to specify the canister id of the canister you're trying to call.
Then, in our own actor we would call if that way.
您会注意到,您需要指定您尝试调用的容器的容器 ID。
然后,在我们自己的actor中,我们会这样调用。
actor { let other_canister : actor {
hello : () -> async Text
} = actor("CANISTER_ID");
public func test() : async Text { return(await other_canister.hello()) }; }
您还会注意到,我们需要等待对另一个canister容器的调用。
Q06:变体类型、结果类型、HTTP 请求和容器间消息