This F# code is failing to compile:
let DoubleString (str : string) = str + str
let ExampleSetString (str : string byref) =
async {
str <- DoubleString str
return 1
}
I get the following error message on the line str <- DoubleString str
:
The byref-typed variable 'str' is used in an invalid way. Byrefs cannot be captured by closures or passed to inner functions
I must take a byref variable, pass it into some pure functions within a computation expression and then set the value of this byref variable. How can I do this?
Dotnet restricts storing byref variables into fields, because byref can point at
&ar[42]
)&myObject.MyField
)&myLocalVar
)&(System.Runtime.CompilerServices.Unsafe.AsRef<myStruct> myVar)
)This leads to a lifetime problem: reference can outlive variable it's pointing at. It won't happen with case 1, but can happen with all other items. For example given function Foo
calling Bar
and passing value by reference will produce this stack
Baz function Foo function Bar function
/ \ / \ / \
--------------|---a----------|----b---------
Foo
have called Bar
and passed it a
as reference &a
. Bar
have stored address of a
into variable b
. This means that b
is byref-variable, updates on this variable will change value in a
. When function completes it's work, memory is freed and can be reused by other function.
Baz function Egg function
/ \ / \
--------------|---c----------
Baz
have called Egg
function, and now at memory where was a
now lies c
. Attempting to change reference to a
now can cause all sort of memory problems, from AccessViolation
(also known as Segmentation Fault in Linux), to data corruption. Worst thing that can happen when memory is corrupted, is for program to continue it's work.
That's the reason why byref variables cannot be captured into closures, which are produced by computation expression.
Now let's return to actual problem - store string intro Azure blob. I'm not familiar with it, but I've found example for this, which can be adapted to following
let DoubleString (str : string) = str + str
let ExampleSetString (containerClient : BlobContainerClient) (str : string) =
task {
let client = containerClient.GetBlobClient("path to where to store")
let doubled = DoubleString str
do! client.UploadAsync(BinaryData.FromString(double), ?overwrite=true)
}