Skip to content

S.R.CS.Unsafe and read-only references #23504

@ektrah

Description

@ektrah

Most (if not all) APIs today are defined to accept references with ref rather than ref readonly. This includes all methods of the Unsafe class. This proposal is to provide a method to turn a ref readonly T into a ref T, so that read-only references can be passed to methods that use ref but are known to be only reading them.

 public static partial class Unsafe
 {
     ...
+    public static ref T AsRef<T>(ref readonly T source)
 }
Original proposals (click to expand)

A few proposals for APIs related to the System.Runtime.CompilerServices.Unsafe class and read-only references.

Proposal 1: Change ref T to ref readonly T where possible:

 public static class Unsafe
 {
     public static ref T AddByteOffset<T>(ref T source, System.IntPtr byteOffset)
     public static ref T Add<T>(ref T source, int elementOffset)
     public static void* Add<T>(void* source, int elementOffset)
     public static ref T Add<T>(ref T source, System.IntPtr elementOffset)
-    public static bool AreSame<T>(ref T left, ref T right)
+    public static bool AreSame<T>(ref readonly T left, ref readonly T right)
-    public static void* AsPointer<T>(ref T value)
+    public static void* AsPointer<T>(ref readonly T value)
     public static ref T AsRef<T>(void* source)
     public static T As<T>(object o) where T : class
     public static ref TTo As<TFrom, TTo>(ref TFrom source)
-    public static System.IntPtr ByteOffset<T>(ref T origin, ref T target)
+    public static System.IntPtr ByteOffset<T>(ref readonly T origin, ref readonly T target)
-    public static void CopyBlock(ref byte destination, ref byte source, uint byteCount)
+    public static void CopyBlock(ref byte destination, ref readonly byte source, uint byteCount)
     public static void CopyBlock(void* destination, void* source, uint byteCount)
-    public static void CopyBlockUnaligned(ref byte destination, ref byte source, uint byteCount)
+    public static void CopyBlockUnaligned(ref byte destination, ref readonly byte source, uint byteCount)
     public static void CopyBlockUnaligned(void* destination, void* source, uint byteCount)
-    public static void Copy<T>(void* destination, ref T source)
+    public static void Copy<T>(void* destination, ref readonly T source)
     public static void Copy<T>(ref T destination, void* source)
     public static void InitBlock(ref byte startAddress, byte value, uint byteCount)
     public static void InitBlock(void* startAddress, byte value, uint byteCount)
     public static void InitBlockUnaligned(ref byte startAddress, byte value, uint byteCount)
     public static void InitBlockUnaligned(void* startAddress, byte value, uint byteCount)
     public static T Read<T>(void* source)
     public static T ReadUnaligned<T>(void* source)
-    public static T ReadUnaligned<T>(ref byte source)
+    public static T ReadUnaligned<T>(ref readonly byte source)
     public static int SizeOf<T>()
     public static ref T SubtractByteOffset<T>(ref T source, System.IntPtr byteOffset)
     public static ref T Subtract<T>(ref T source, int elementOffset)
     public static void* Subtract<T>(void* source, int elementOffset)
     public static ref T Subtract<T>(ref T source, System.IntPtr elementOffset)
     public static void Write<T>(void* destination, T value)
     public static void WriteUnaligned<T>(void* destination, T value)
     public static void WriteUnaligned<T>(ref byte destination, T value)
 }

Proposal 2: Unsafe but every ref T is a ref readonly T:

+public static class UnsafeReadOnly
+{
+    public static ref readonly T AddByteOffset<T>(ref readonly T source, System.IntPtr byteOffset)
+    public static ref readonly T Add<T>(ref readonly T source, int elementOffset)
+    public static void* Add<T>(void* source, int elementOffset)
+    public static ref readonly T Add<T>(ref readonly T source, System.IntPtr elementOffset)
+    public static bool AreSame<T>(ref readonly T left, ref readonly T right)
+    public static void* AsPointer<T>(ref readonly T value)
+    public static ref readonly T AsRef<T>(void* source)
+    public static T As<T>(object o) where T : class
+    public static ref readonly TTo As<TFrom, TTo>(ref readonly TFrom source)
+    public static System.IntPtr ByteOffset<T>(ref readonly T origin, ref readonly T target)
+    public static void CopyBlock(ref readonly byte destination, ref readonly byte source, uint byteCount)
+    public static void CopyBlock(void* destination, void* source, uint byteCount)
+    public static void CopyBlockUnaligned(ref readonly byte destination, ref readonly byte source, uint byteCount)
+    public static void CopyBlockUnaligned(void* destination, void* source, uint byteCount)
+    public static void Copy<T>(void* destination, ref readonly T source)
+    public static void Copy<T>(ref readonly T destination, void* source)
+    public static void InitBlock(ref readonly byte startAddress, byte value, uint byteCount)
+    public static void InitBlock(void* startAddress, byte value, uint byteCount)
+    public static void InitBlockUnaligned(ref readonly byte startAddress, byte value, uint byteCount)
+    public static void InitBlockUnaligned(void* startAddress, byte value, uint byteCount)
+    public static T Read<T>(void* source)
+    public static T ReadUnaligned<T>(void* source)
+    public static T ReadUnaligned<T>(ref readonly byte source)
+    public static int SizeOf<T>()
+    public static ref readonly T SubtractByteOffset<T>(ref readonly T source, System.IntPtr byteOffset)
+    public static ref readonly T Subtract<T>(ref readonly T source, int elementOffset)
+    public static void* Subtract<T>(void* source, int elementOffset)
+    public static ref readonly T Subtract<T>(ref readonly T source, System.IntPtr elementOffset)
+    public static void Write<T>(void* destination, T value)
+    public static void WriteUnaligned<T>(void* destination, T value)
+    public static void WriteUnaligned<T>(ref readonly byte destination, T value)
+}

Proposal 3: A method to turn a ref readonly T into a ref T:

 public static partial class Unsafe
 {
     ...
+    public static ref T AsRef<T>(ref readonly T source)
 }

Potential problems:

Passing a ref readonly T argument does not look different from passing a T argument, so the following code is legal but very surprising:

byte[] buffer = new byte[16];
Unsafe.CopyBlockUnaligned(ref buffer[0], 123, 16); // what does this copy?

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-approvedAPI was approved in API review, it can be implementedarea-System.Runtime.CompilerServicesgood first issueIssue should be easy to implement, good for first-time contributorshelp wanted[up-for-grabs] Good issue for external contributors

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions