-
Notifications
You must be signed in to change notification settings - Fork 789
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ByRefKind.InOut
treats as ByRefKind.In
in SpanAction delegate
#5856
Comments
Refactored a bit to show the delegate signature and delegate creation. But The closest thing I can imagine to "modify" a readonly structs is "Evil Struct Replacement" (fsharp/fslang-design#287 (comment)) but this is no longer possible in F# for legitimate reasons. module Test
open System
open System.Text
open System.Buffers
let stringF = fun (span: Span<char>) (sequence: ReadOnlySequence<byte>) ->
for segment in sequence do
Encoding.ASCII.GetChars(segment.Span, span) |> ignore
//span <- span.Slice segment.Length
let stringD : SpanAction<char,ReadOnlySequence<byte>> = new SpanAction<char,ReadOnlySequence<byte>>(stringF)
let getAsciiString(buffer: ReadOnlySequence<byte>) =
if buffer.IsSingleSegment then
Encoding.ASCII.GetString buffer.First.Span
else
String.Create(int buffer.Length, buffer, stringD)
[<EntryPoint>]
let main argv =
let bytes = "abcdefg"B
let span = ReadOnlySequence<byte>(bytes)
let s = getAsciiString span
0 The String.Create signature looks like this: public static string Create<TState>(int length, TState state, SpanAction<char, TState> action)
{
throw null;
} The SpanAction signature looks like this: namespace System.Buffers
{
public delegate void SpanAction<T, in TArg>(Span<T> span, TArg arg);
} Related issues: "string.Create(int length, TState state, SpanAction<char, TState> action) formalizes the mutate newly allocated zero'd string prior to use pattern; while introducing safety via the Span and ensuring its not used prior to mutation." Also: "This method is implemented to allocate the string and then hand out a writable span you can write to in order to fill in the contents of the string while it’s being constructed. Note that the stack-only nature of Span is beneficial in this case, guaranteeing that the span (which refers to the string’s internal storage) will cease to exist before the string’s constructor completes, making it impossible to use the span to mutate the string after the construction is complete" |
This is by design, as values are immutable by default in F#. A more direct translation: let getAsciiString(buffer: ReadOnlySequence<byte>) =
if buffer.IsSingleSegment then
Encoding.ASCII.GetString buffer.First.Span
else
String.Create(int buffer.Length, buffer, fun span sequence ->
for segment in sequence do
Encoding.ASCII.GetChars(segment.Span, span) |> ignore
span <- span.Slice(segment.Length)
) Yields that error message:
Similarly, the
I recommend a different approach to generating a |
@Szer @cartermp It seems that the missing piece was a Roslyn optimization for local function argument mutation as of https://github.com/dotnet/corefx/issues/32563#issuecomment-435566555. open System
open System.Buffers
module Test = begin
open System
open System.Text
open System.Buffers
let getAsciiString(buffer: ReadOnlySequence<byte>) =
if buffer.IsSingleSegment then
Encoding.ASCII.GetString buffer.First.Span
else
String.Create(int buffer.Length, buffer, fun span sequence ->
let mutable localSpan = span
for segment in sequence do
Encoding.ASCII.GetChars(segment.Span, localSpan) |> ignore
localSpan <- localSpan.Slice segment.Length
)
end
[<EntryPoint>]
let main argv =
let bytes = "Hello World from F#!"B
let ros = ReadOnlySequence<byte>(bytes)
let s = Test.getAsciiString ros
printfn "Result: %s" s
0 // return an integer exit code
|
This article about Span in C# contains following snippet:
which translates to something like this in F#:
Code in C# compiles fine, but F# throws an error:
The type ByRefKinds.InOut doesn't match the type ByRefKinds.In
This also won't work:
with error:
byref point is readonly, so this write is not permitted
Repro steps
create
Program.fs
:dotnet build
Expected behavior
It should be possible to compile C# snippet in F#
Actual behavior
Incoming span argument is
InRef
-like so it's not possible to write into itKnown workarounds
Use C#
Related information
Provide any related information
The text was updated successfully, but these errors were encountered: