byref return in F# 4.5
Asked Answered
L

2

9

I am trying to add a F#-style interface to a type, that has a byref return method. Here's the code:

type IPool<'P, 'T when 'T: struct> =
  abstract member GetReference: ITypedPointer<'P, 'T> -> byref<'T>

let Ref<'TPool, 'P, 'T when 'TPool :> IPool<'P, 'T>> (pool: 'TPool) pointer =
  pool.GetReference pointer

Now to my surprise, a similar thing worked fine until I introduced IPool interface. Before that Ref itself contained an implementation like &pool.data.[idx], and worked fine.

I tried installing nightly build of F# Tools, cause latest release does not officially support byref returns, and PR to introduce them was recently completed: https://github.com/Microsoft/visualfsharp/pull/4888

However, I still get error FS3209: The address of the variable 'copyOfStruct' cannot be used at this point. A method or function may not return the address of this local value. in Visual Studio. Type outref<T> still does not seem to be available either. Am I missing something?

I also tried to drop the pointer parameter, and just return pool.GetReference to only get a different error message.

Addition: the ultimate goal is to be able to do

let aref = Ref pool ptr
let bref = Ref pool ptr
aref <- 42
assert(aref = bref)

e.g. give caller a direct reference to an internal memory, usually backed by an array, similar to Span<T>. I am making this for performance reasons, so it is not OK to allocate on every call to Ref.

Lasandralasater answered 25/6, 2018 at 23:36 Comment(0)
L
2

For some reason, reducing generalization helped to get rid of the error:

let Ref<'P, 'T when 'T: struct> (pool: IPool<'P, 'T>) pointer = pool.GetReference pointer

Solution provided by

https://github.com/Microsoft/visualfsharp/issues/5366#issuecomment-407521220

Though it does not explain why the original code does not compile.

Lasandralasater answered 24/7, 2018 at 19:25 Comment(0)
E
-1

I don't think it's standard practice to return a byref type. This type is really meant for method parameters, mostly for C# interop with out or ref parameters. Take a look at this StackOverflow question for a good explanation.

What you can do is change the method on your interface to take a tuple of ITypedPointer<'P,'T> and byref<'T> (usage of byref is not allowed with curried parameters) and return unit instead. Then you can call GetReference like any standard .NET method with an out parameter in C#. That would look something like this:

type ITypedPointer<'P, 'T> = interface end

type IPool<'P, 'T when 'T: struct> =
  abstract member GetReference: ITypedPointer<'P, 'T> * byref<'T> -> unit

let Ref<'TPool, 'P, 'T when 'TPool :> IPool<'P, 'T>> (pool: 'TPool) pointer =
  let mutable value = Unchecked.defaultof<'T>
  pool.GetReference(pointer, &value)
  value
Eire answered 26/6, 2018 at 12:5 Comment(4)
The whole idea is to return byref so that method user can do stuff like: let x = Ref pool ptr; x <- 10Lasandralasater
You can actually find example at the very bottom of this file: github.com/lostmsu/ShortPointers/blob/…Lasandralasater
If you just want to mutate it from another location, why not use a standard F# ref Reference Cell?Eire
Because I need the client to be able to get a reference to an array element. let x = [| 1 |]; let r1 = ref x.[0]; let r2 = ref x.[0]; r1 := 2; !r2 will yield 1. If I use &x.[0] instead, it will be 2Lasandralasater

© 2022 - 2024 — McMap. All rights reserved.