diff --git a/doc/docs.md b/doc/docs.md index 70994982f9..6f99213d16 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -70,6 +70,7 @@ you can do in V. * [Advanced](#advanced) * [Calling C functions from V](#calling-c-functions-from-v) + * [Debugging generated C code](#debugging-generated-c-code) * [Conditional compilation](#conditional-compilation) * [Compile time pseudo variables](#compile-time-pseudo-variables) * [Reflection via codegen](#reflection-via-codegen) @@ -174,6 +175,7 @@ fn foo() (int, int) { a, b := foo() println(a) // 2 println(b) // 3 +c, _ := foo() // ignore values using `_` ``` Functions can return multiple values. @@ -1329,7 +1331,7 @@ sum := World(Moon{}) ``` To check whether a sum type instance holds a certain type, use `sum is Type`. -To cast a sum type to one of its variants you use `sum as Type`: +To cast a sum type to one of its variants you can use `sum as Type`: ```v fn (m Mars) dust_storm() bool @@ -1347,6 +1349,8 @@ fn main() { } ``` +### Matching sum types + You can also use `match` to determine the variant: ```v @@ -1401,6 +1405,7 @@ Note: shadowing only works when the match expression is a variable. It will not ### Option/Result types and error handling +Option types are declared with `?Type`: ```v struct User { id int @@ -1411,12 +1416,6 @@ struct Repo { users []User } -fn new_repo() Repo { - return Repo { - users: [User{1, 'Andrew'}, User {2, 'Bob'}, User {10, 'Charles'}] - } -} - fn (r Repo) find_user_by_id(id int) ?User { for user in r.users { if user.id == id { @@ -1428,7 +1427,9 @@ fn (r Repo) find_user_by_id(id int) ?User { } fn main() { - repo := new_repo() + repo := Repo { + users: [User{1, 'Andrew'}, User {2, 'Bob'}, User {10, 'Charles'}] + } user := repo.find_user_by_id(10) or { // Option types must be handled by `or` blocks return } @@ -1458,29 +1459,38 @@ user := repo.find_user_by_id(7) or { } ``` -There are three ways of handling an optional. +### Handling optionals -The first method is to propagate the error: +There are three ways of handling an optional. The first method is to +propagate the error: ```v -resp := http.get(url)? -println(resp.text) -``` +import net.http -`http.get` returns `?http.Response`. Because it was called with `?`, the error will be propagated to the calling function -(which must return an optional as well). If it is used in the `main()` function it will `panic` instead, since the error -cannot be propagated any further. - -The code above is essentially a condensed version of - -```v -resp := http.get(url) or { - return error(err) +fn f(url string) ?string { + resp := http.get(url)? + return resp.text } -println(resp.text) ``` -The second is to break from execution early: +`http.get` returns `?http.Response`. Because `?` follows the call, the +error will be propagated to the caller of `f`. When using `?` after a +function call producing an optional, the enclosing function must return +an optional as well. If error propagation is used in the `main()` +function it will `panic` instead, since the error cannot be propagated +any further. + +The body of `f` is essentially a condensed version of: + +```v + resp := http.get(url) or { + return error(err) + } + return resp.text +``` + +--- +The second method is to break from execution early: ```v user := repo.find_user_by_id(7) or { @@ -1492,8 +1502,9 @@ Here, you can either call `panic()` or `exit()`, which will stop the execution o or use a control flow statement (`return`, `break`, `continue`, etc) to break from the current block. Note that `break` and `continue` can only be used inside a `for` loop. +--- The third method is to provide a default value at the end of the `or` block. In case of an error, -that value would be assigned instead, so it must have the same type as the `Option` being handled. +that value would be assigned instead, so it must have the same type as the content of the `Option` being handled. ```v fn do_something(s string) ?string { @@ -1506,7 +1517,7 @@ b := do_something('bar') or { 'default' } // b will be 'default' ``` V does not have a way to forcibly "unwrap" an optional (as other languages do, for instance Rust's `unwrap()` -or Swift's `!`). To do this use `or { panic(err) }` instead. +or Swift's `!`). To do this, use `or { panic(err) }` instead. ## Generics @@ -1844,6 +1855,8 @@ fn main() { } ``` +### #flag + Add `#flag` directives to the top of your V files to provide C compilation flags like: - `-I` for adding C include files search paths @@ -1863,6 +1876,8 @@ NB: Each flag must go on its own line (for now) #flag linux -DIMGUI_IMPL_API= ``` +### Including C code + You can also include C code directly in your V module. For example, let's say that your C code is located in a folder named 'c' inside your module folder. Then: * Put a v.mod file inside the toplevel folder of your module (if you @@ -1885,26 +1900,28 @@ Module { #include "header.h" ``` NB: @VROOT will be replaced by V with the *nearest parent folder, where there is a v.mod file*. -Any .v file beside or below the folder where the v.mod file is, can use #flag @VROOT/abc to refer to this folder. +Any .v file beside or below the folder where the v.mod file is, can use `#flag @VROOT/abc` to refer to this folder. The @VROOT folder is also *prepended* to the module lookup path, so you can *import* other modules under your @VROOT, by just naming them. -The instructions above will make V look for an compiled .o file in your module folder/c/implementation.o . +The instructions above will make V look for an compiled .o file in your module `folder/c/implementation.o`. If V finds it, the .o file will get linked to the main executable, that used the module. If it does not find it, V assumes that there is a `@VROOT/c/implementation.c` file, and tries to compile it to a .o file, then will use that. This allows you to have C code, that is contained in a V module, so that its distribution is easier. -You can see a complete example for using C code in a V wrapper module here: -[minimal V project, that has a module, which contains C code](https://github.com/vlang/v/tree/master/vlib/v/tests/project_with_c_code) +You can see a complete minimal example for using C code in a V wrapper module here: +[project_with_c_code](https://github.com/vlang/v/tree/master/vlib/v/tests/project_with_c_code). You can use `-cflags` to pass custom flags to the backend C compiler. You can also use `-cc` to change the default C backend compiler. For example: `-cc gcc-9 -cflags -fsanitize=thread`. +### C types + Ordinary zero terminated C strings can be converted to V strings with `string(cstring)` or `string(cstring, len)`. -NB: `string/1` and `string/2` do NOT create a copy of the `cstring`, so you should NOT free it after calling `string()`. If you need to make a copy of the C string (some libc APIs like `getenv/1` pretty much require that, since they -return pointers to internal libc memory), you can use: `cstring_to_vstring(cstring)` +NB: Each `string(...)` function does NOT create a copy of the `cstring`, so you should NOT free it after calling `string()`. If you need to make a copy of the C string (some libc APIs like `getenv` pretty much require that, since they +return pointers to internal libc memory), you can use `cstring_to_vstring(cstring)`. On Windows, C APIs often return so called `wide` strings (utf16 encoding). These can be converted to V strings with `string_from_wide(&u16(cwidestring))` . @@ -1920,7 +1937,9 @@ To cast a `voidptr` to a V reference, use `user := &User(user_void_ptr)`. `voidptr` can also be dereferenced into a V struct through casting: `user := User(user_void_ptr)`. -[Socket.v has an example which calls C code from V](https://github.com/vlang/v/blob/master/vlib/net/socket.v) . +[socket.v has an example which calls C code from V](https://github.com/vlang/v/blob/master/vlib/net/socket.v) . + +## Debugging generated C code To debug issues in the generated C code, you can pass these flags: