1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

docs: small fixes in the doc (#10840)

This commit is contained in:
Uwe Krüger 2021-07-17 16:47:42 +02:00 committed by GitHub
parent d84ffbf73c
commit 6bf69ea3e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -3687,14 +3687,14 @@ Due to performance considerations V tries to put objects on the stack if possibl
but allocates them on the heap when obviously necessary. Example: but allocates them on the heap when obviously necessary. Example:
```v ```v
struct RefStruct {
r &MyStruct
}
struct MyStruct { struct MyStruct {
n int n int
} }
struct RefStruct {
r &MyStruct
}
fn main() { fn main() {
q, w := f() q, w := f()
println('q: $q.r.n, w: $w.n') println('q: $q.r.n, w: $w.n')
@ -3751,7 +3751,7 @@ fn (mut a MyStruct) f(b &MyStruct) int {
Here the call `q.f(&w)` passes references to `q` and `w` because `a` is Here the call `q.f(&w)` passes references to `q` and `w` because `a` is
`mut` and `b` is of type `&MyStruct` in `f()`'s declaration, so technically `mut` and `b` is of type `&MyStruct` in `f()`'s declaration, so technically
these references are leaving `main()`. However the *lifetime* of these these references are leaving `main()`. However the *lifetime* of these
references lies inside the scop of `main()` so `q` and `w` are allocated references lies inside the scope of `main()` so `q` and `w` are allocated
on the stack. on the stack.
#### Manual Control for Stack and Heap #### Manual Control for Stack and Heap
@ -3759,10 +3759,10 @@ on the stack.
In the last example the V compiler could put `q` and `w` on the stack In the last example the V compiler could put `q` and `w` on the stack
because it assumed that in the call `q.f(&w)` these references were only because it assumed that in the call `q.f(&w)` these references were only
used for reading and modifying the referred values – and not to pass the used for reading and modifying the referred values – and not to pass the
references itself somewhere else. This can be seen in a way that the references themselves somewhere else. This can be seen in a way that the
references to `q` and `w` are only *borrowed* to `f()`. references to `q` and `w` are only *borrowed* to `f()`.
Things become different if `f()` is doing something with the references itself: Things become different if `f()` is doing something with a reference itself:
```v ```v
struct RefStruct { struct RefStruct {
@ -3806,7 +3806,7 @@ refer to an object stored on stack"*. The assumption made in `g()` that the call
A solution to this dilemma is the `[heap]` attribute at the declaration of A solution to this dilemma is the `[heap]` attribute at the declaration of
`struct MyStruct`. It instructs the compiler to *always* allocate `MyStruct`-objects `struct MyStruct`. It instructs the compiler to *always* allocate `MyStruct`-objects
on the heap. This way the references to `s` remains valid even after `g()` returns. on the heap. This way the reference to `s` remains valid even after `g()` returns.
The compiler takes into consideration that `MyStruct` objects are always heap The compiler takes into consideration that `MyStruct` objects are always heap
allocated when checking `f()` and allows assigning the reference to `s` to the allocated when checking `f()` and allows assigning the reference to `s` to the
`r.r` field. `r.r` field.
@ -3820,8 +3820,8 @@ fn (mut a MyStruct) f() &MyStruct {
} }
``` ```
Here `f()` is passed a reference `a` that is passed back to the caller and returned Here `f()` is passed a reference `a` as receiver that is passed back to the caller and returned
at the same time. The intention behind such a declaration is method chaining like as result at the same time. The intention behind such a declaration is method chaining like
`y = x.f().g()`. However, the problem with this approach is that a second reference `y = x.f().g()`. However, the problem with this approach is that a second reference
to `a` is created – so it is not only borrowed and `MyStruct` has to be to `a` is created – so it is not only borrowed and `MyStruct` has to be
declared as `[heap]`. declared as `[heap]`.
@ -3861,26 +3861,16 @@ There is an alternative way to manually control allocation on a case to case bas
approach is not recommended but shown here for the sake of completeness: approach is not recommended but shown here for the sake of completeness:
```v ```v
struct MyStruct {
n int
}
struct RefStruct { struct RefStruct {
mut: mut:
r &MyStruct r &MyStruct
} }
struct MyStruct { // simple function - just to overwrite stack segment previously used by `g()`
n int
}
fn (mut r RefStruct) f(s &MyStruct) {
r.r = unsafe { s } // override compiler check
}
fn (mut r RefStruct) g() {
s := &MyStruct{ // `s` explicitly referes to a heap object
n: 7
}
r.f(s)
}
fn use_stack() { fn use_stack() {
x := 7.5 x := 7.5
y := 3.25 y := 3.25
@ -3897,6 +3887,19 @@ fn main() {
use_stack() // to erase invalid stack contents use_stack() // to erase invalid stack contents
println('r: $r') println('r: $r')
} }
fn (mut r RefStruct) g() {
s := &MyStruct{ // `s` explicitly refers to a heap object
n: 7
}
// change `&MyStruct` -> `MyStruct` above and `r.f(s)` -> `r.f(&s)` below
// to see data in stack segment being overwritten
r.f(s)
}
fn (mut r RefStruct) f(s &MyStruct) {
r.r = unsafe { s } // override compiler check
}
``` ```
Here the compiler check is suppressed by the `unsafe` block. To make `s` be heap Here the compiler check is suppressed by the `unsafe` block. To make `s` be heap