Search code examples
.netf#ref

F# return byref from CollectionsMarshal.GetValueRefOrAddDefault


I am trying to get the byref<int> that is returned from the CollectionMarshal.GetValueRefOrAddDefault method. Right now t is an int but I am wanting the byref<int>.

open System.Runtime.InteropServices
let d = System.Collections.Generic.Dictionary<int, int>()

let t, _ = CollectionsMarshal.GetValueRefOrAddDefault (d, 1)

The F# docs say to prepend a & to the method call but then the compiler returns an error.

let t, _ = &CollectionsMarshal.GetValueRefOrAddDefault (d, 1)
// Error:
// Cannot take the address of the value returned from the expression.
// Assign the returned value to a let-bound value before taking the address.
// F# Compiler3236

Solution

  • Here's some sample code to illustrate a solution:

    open System.Runtime.InteropServices
    
    // without this you get a warning about the byref-typed `v` being a top-level value in a module
    type Blah() = 
        let d = System.Collections.Generic.Dictionary<int, int>()
        let mutable t = Unchecked.defaultof<_>
        let v = &CollectionsMarshal.GetValueRefOrAddDefault (d, 1, &t)
    

    What's happening here is that you're trying to de-sugar what the compiler is trying to sugar (implicit mutable placeholder parameters), but there's a gap here IMO in the ways the compiler sugars things. You could make a reasonable argument that let struct(wasAdded, value) = CollectionsMarshal.GetValueRefOrAddDefault(d, 1) could be a reasonable desugaring, due to no allocations, etc, etc.

    Anyway, in your example let's look at the signature of the member you're trying to invoke:

    GetValueRefOrAddDefault : System.Collections.Generic.Dictionary<'Key, 'Value> * 'Key * byref<bool> -> byref<'Value>
    

    The compiler has a pattern for methods that have parameter lists that end with out/byref values and return byref values: it will do all of the bookkeeping for you. By this I mean it will initialize the let mutable x = Unchecked.defaultof<_> cells for you, call the method with the ref values to those cells, and give you the final answers.

    Because you want to do fast things, you just need to do that wrapping yourself instead of letting the compiler desugar.