doc: enhance the Option example, tweak the Calling C section (#5830)

pull/5825/head
Nick Treleaven 2020-07-14 18:24:59 +01:00 committed by GitHub
parent cf7d03bda6
commit f5579525c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 52 additions and 33 deletions

View File

@ -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: