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.

LOST
  • 2,956
  • 3
  • 25
  • 40

2 Answers2

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.

LOST
  • 2,956
  • 3
  • 25
  • 40
-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
Aaron M. Eshbach
  • 6,380
  • 12
  • 22
  • 2
    The whole idea is to return byref so that method user can do stuff like: `let x = Ref pool ptr; x <- 10` – LOST Jun 26 '18 at 17:58
  • 2
    You can actually find example at the very bottom of this file: https://github.com/lostmsu/ShortPointers/blob/5c700ee4bf7c79722c6448f9436b5ba4027c44ea/src/StructPool.fs – LOST Jun 26 '18 at 18:00
  • If you just want to mutate it from another location, why not use a standard F# `ref` Reference Cell? – Aaron M. Eshbach Jun 26 '18 at 18:08
  • 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 `2` – LOST Jun 26 '18 at 18:48