2

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?

Kurren
  • 827
  • 1
  • 9
  • 18
  • 2
    It's not possible, not allowed by runtime due to memory safety. You'll have to reconsider design around using byref and replace with setter callback (`setStr: string -> unit`) or something else instead – JL0PD Jun 25 '22 at 12:36
  • @JL0PD when setting a blob storage string in azure functions, the easiest way is to use out parameters. I need to use the task computation, so there’s no way of doing this other than using a more complicated blob storage output binding? – Kurren Jun 25 '22 at 22:32

1 Answers1

5

Dotnet restricts storing byref variables into fields, because byref can point at

  1. array element (&ar[42])
  2. field of object (&myObject.MyField)
  3. local variable (&myLocalVar)
  4. unmanaged memory (&(System.Runtime.CompilerServices.Unsafe.AsRef<myStruct> myVar))
  5. probably more

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)
    }
JL0PD
  • 3,698
  • 2
  • 15
  • 23