sain.maybe_uninit
A wrapper type to construct uninitialized instances of T.
1# BSD 3-Clause License 2# 3# Copyright (c) 2022-Present, nxtlo 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 9# * Redistributions of source code must retain the above copyright notice, this 10# list of conditions and the following disclaimer. 11# 12# * Redistributions in binary form must reproduce the above copyright notice, 13# this list of conditions and the following disclaimer in the documentation 14# and/or other materials provided with the distribution. 15# 16# * Neither the name of the copyright holder nor the names of its 17# contributors may be used to endorse or promote products derived from 18# this software without specific prior written permission. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30"""A wrapper type to construct uninitialized instances of T.""" 31 32from __future__ import annotations 33 34import typing 35 36from . import macros 37from .macros import rustc_diagnostic_item 38 39if typing.TYPE_CHECKING: 40 import collections.abc as collections 41 42 from typing_extensions import Self 43 44T = typing.TypeVar("T") 45 46 47@rustc_diagnostic_item("MaybeUninit") 48@typing.final 49class MaybeUninit(typing.Generic[T]): 50 """A wrapper type to construct uninitialized instances of `T`. 51 52 This is kinda equivalent to Rust's `MaybeUninit<T>` wrapper. 53 54 The difference is what's happening under the hood is when initializing an instance of this object, 55 nothing really is being initialized, until you call `.write()` to initialize the inner value. 56 57 What?? 58 ----- 59 Ok, so `MaybeUninit<T>` is in a sense the unsafe equivalent of `Option<T>`, it either contains a value of type `T`, 60 or it contains uninitialized memory (`the attribute doesn't exist`). 61 62 However, `MaybeUninit` is unable to tell you whether the value it contains is `Some(T)` or `None`, So you 63 as a programmer is responsible for initializing it. 64 65 And by default, `Option<T>` always contain a default value, which is `None`. 66 67 Examples 68 -------- 69 ```py 70 # Let's write a simple wrapper around `MaybeUninit<T>` to create a dynamic array. 71 class SizedArray[T]: 72 def __init__(self, size: int) -> None: 73 # Creates count `size` of `MaybeUninit<T>` in an uninitialized state. 74 self.buf = MaybeUninit[T].uninit_array(size) 75 # Track how many initialized elements are in the array. 76 self.len = 0 77 78 def push(self, value: T) -> None: 79 # if the length of the array is equal to the size of the array, 80 # then we've reached the max size of the array. 81 if self.len == len(self.buf): 82 raise RuntimeError("max size reached") 83 84 # get the next uninitialized element and write into it. 85 self.buf[self.len].write(value) 86 self.len += 1 87 88 def copy_from(self, other: Sequence[T]) -> None: 89 # get the remaining space in the array. 90 spared = self.buf[self.len:] 91 other_len = len(other) 92 # Check if there's enough capacity to copy the other list into the array. 93 if len(spared) < other_len: 94 raise RuntimeError("not enough space") 95 96 # Copy the other list into the array. 97 index = 0 98 while index < other_len: 99 self.buf[self.len + index].write(other[index]) 100 index += 1 101 102 self.len += other_len 103 104 def read(self) -> Sequence[T]: 105 return MaybeUninit.array_assume_init(self.buf[:self.len]) 106 107 array = SizedArray[int](3) 108 array.push(1) 109 array.copy_from([2, 3]) 110 assert array.read() == (1, 2, 3) 111 ``` 112 """ 113 114 __slots__ = ("__value",) 115 116 @typing.overload 117 def __init__(self) -> None: ... 118 119 @typing.overload 120 def __init__(self, value: T) -> None: 121 """Creates a new MaybeUninit<T> initialized with the given value. 122 It is safe to call assume_init on the return value of this function. 123 124 Example 125 ------- 126 ```py 127 v = MaybeUninit(1) 128 assert v.assume_init() == 1 129 ``` 130 """ 131 132 def __init__(self, value: T | None = None) -> None: 133 if value is None: 134 return None 135 else: 136 # we simply pre-initialize the value if it was passed 137 # when constructing the instance. 138 self.__value = value 139 140 @classmethod 141 def uninit(cls) -> Self: 142 """Creates a new `MaybeUninit<T>` in an uninitialized state. 143 144 Example 145 ------- 146 ```py 147 v: MaybeUninit[str] = MaybeUninit.uninit() # or just MaybeUninit() 148 ``` 149 """ 150 return cls() 151 152 @classmethod 153 def uninit_array(cls, n: int) -> collections.Sequence[Self]: 154 """Creates an immutable sequence of `MaybeUninit<T>` in an uninitialized state. 155 156 Example 157 ------- 158 ```py 159 v = MaybeUninit[str].uninit_array(3) 160 assert len(v) == 3 161 for uninit in v: 162 uninit.write('content') 163 164 initialized = uninit.assume_init() 165 ``` 166 """ 167 return tuple(cls() for _ in range(n)) 168 169 @macros.unsafe 170 def assume_init(self) -> T: 171 """Get the inner value, assuming that it was initialized by the caller. 172 173 It is unsafe and undefined behavior to call this method on an uninitialized state, 174 175 Example 176 ------- 177 ```py 178 uninit: MaybeUninit[int] = MaybeUninit.uninit() 179 val = uninit.assume_init() # This is UNSAFE ⚠️ 180 181 # Initialize it first. 182 uninit.write(0) 183 val = uninit.assume_init() # This is safe to access. 184 ``` 185 """ 186 # SAFETY: the caller must guarantee that `self` is initialized. 187 return self.__value 188 189 @staticmethod 190 @macros.unsafe 191 def array_assume_init( 192 array: collections.Sequence[MaybeUninit[T]], 193 ) -> collections.Sequence[T]: 194 """Extracts a sequence of `MaybeUninit[T]` containers. 195 196 You must guarantee that all elements in the array are initialized. 197 198 Example 199 ------- 200 ```py 201 array: list[MaybeUninit[int]] = [MaybeUninit(), MaybeUninit(), MaybeUninit()] 202 array[0].write(0) 203 array[1].write(1) 204 array[2].write(2) 205 # transposed into a tuple. 206 assert MaybeUninit.array_assume_init(array) == (0, 1, 2) 207 ``` 208 """ 209 # SAFETY: The caller guarantees that all elements of the array are initialized 210 return tuple(uninit.assume_init() for uninit in array) 211 212 @staticmethod 213 @macros.unsafe 214 def array_assume_init_mut( 215 array: collections.Sequence[MaybeUninit[T]], 216 ) -> collections.MutableSequence[T]: 217 """Extracts a sequence of `MaybeUninit[T]` to a list of `T`s. 218 219 You must guarantee that all elements in the array are initialized. 220 221 Example 222 ------- 223 ```py 224 array: list[MaybeUninit[int]] = [MaybeUninit(), MaybeUninit(), MaybeUninit()] 225 array[0].write(0) 226 array[1].write(1) 227 array[2].write(2) 228 # transposed into a list. 229 assert MaybeUninit.array_assume_init_mut(array) == [0, 1, 2] 230 ``` 231 """ 232 # SAFETY: The caller guarantees that all elements of the array are initialized 233 return [uninit.assume_init() for uninit in array] 234 235 def write(self, value: T) -> T: 236 """Sets the value of the `MaybeUninit[T]`, Then return a reference to it. 237 238 This will overwrite any previous values, if was initialized. 239 240 Example 241 ------- 242 ```py 243 buffer: MaybeUninit[bytes] = MaybeUninit() 244 buffer.write(b"Hello, World!") 245 print(buffer.assume_init()) 246 ``` 247 """ 248 self.__value = value 249 # SAFETY: we just initialized the value, so it's safe to access. 250 return self.__value 251 252 def __repr__(self) -> str: 253 if self: 254 return f"MaybeUninit(value: {self.__value!r})" 255 return "<uninit>" 256 257 __str__ = __repr__ 258 259 def __bool__(self) -> bool: 260 """Perform a boolean check on whether the value is initialized or not. 261 262 Example 263 ------- 264 ```py 265 v = MaybeUninit[bool]() 266 assert not v 267 ``` 268 """ 269 return hasattr(self, "_MaybeUninit__value") 270 271 def __hash__(self) -> int: 272 return self.__value.__hash__()
48@rustc_diagnostic_item("MaybeUninit") 49@typing.final 50class MaybeUninit(typing.Generic[T]): 51 """A wrapper type to construct uninitialized instances of `T`. 52 53 This is kinda equivalent to Rust's `MaybeUninit<T>` wrapper. 54 55 The difference is what's happening under the hood is when initializing an instance of this object, 56 nothing really is being initialized, until you call `.write()` to initialize the inner value. 57 58 What?? 59 ----- 60 Ok, so `MaybeUninit<T>` is in a sense the unsafe equivalent of `Option<T>`, it either contains a value of type `T`, 61 or it contains uninitialized memory (`the attribute doesn't exist`). 62 63 However, `MaybeUninit` is unable to tell you whether the value it contains is `Some(T)` or `None`, So you 64 as a programmer is responsible for initializing it. 65 66 And by default, `Option<T>` always contain a default value, which is `None`. 67 68 Examples 69 -------- 70 ```py 71 # Let's write a simple wrapper around `MaybeUninit<T>` to create a dynamic array. 72 class SizedArray[T]: 73 def __init__(self, size: int) -> None: 74 # Creates count `size` of `MaybeUninit<T>` in an uninitialized state. 75 self.buf = MaybeUninit[T].uninit_array(size) 76 # Track how many initialized elements are in the array. 77 self.len = 0 78 79 def push(self, value: T) -> None: 80 # if the length of the array is equal to the size of the array, 81 # then we've reached the max size of the array. 82 if self.len == len(self.buf): 83 raise RuntimeError("max size reached") 84 85 # get the next uninitialized element and write into it. 86 self.buf[self.len].write(value) 87 self.len += 1 88 89 def copy_from(self, other: Sequence[T]) -> None: 90 # get the remaining space in the array. 91 spared = self.buf[self.len:] 92 other_len = len(other) 93 # Check if there's enough capacity to copy the other list into the array. 94 if len(spared) < other_len: 95 raise RuntimeError("not enough space") 96 97 # Copy the other list into the array. 98 index = 0 99 while index < other_len: 100 self.buf[self.len + index].write(other[index]) 101 index += 1 102 103 self.len += other_len 104 105 def read(self) -> Sequence[T]: 106 return MaybeUninit.array_assume_init(self.buf[:self.len]) 107 108 array = SizedArray[int](3) 109 array.push(1) 110 array.copy_from([2, 3]) 111 assert array.read() == (1, 2, 3) 112 ``` 113 """ 114 115 __slots__ = ("__value",) 116 117 @typing.overload 118 def __init__(self) -> None: ... 119 120 @typing.overload 121 def __init__(self, value: T) -> None: 122 """Creates a new MaybeUninit<T> initialized with the given value. 123 It is safe to call assume_init on the return value of this function. 124 125 Example 126 ------- 127 ```py 128 v = MaybeUninit(1) 129 assert v.assume_init() == 1 130 ``` 131 """ 132 133 def __init__(self, value: T | None = None) -> None: 134 if value is None: 135 return None 136 else: 137 # we simply pre-initialize the value if it was passed 138 # when constructing the instance. 139 self.__value = value 140 141 @classmethod 142 def uninit(cls) -> Self: 143 """Creates a new `MaybeUninit<T>` in an uninitialized state. 144 145 Example 146 ------- 147 ```py 148 v: MaybeUninit[str] = MaybeUninit.uninit() # or just MaybeUninit() 149 ``` 150 """ 151 return cls() 152 153 @classmethod 154 def uninit_array(cls, n: int) -> collections.Sequence[Self]: 155 """Creates an immutable sequence of `MaybeUninit<T>` in an uninitialized state. 156 157 Example 158 ------- 159 ```py 160 v = MaybeUninit[str].uninit_array(3) 161 assert len(v) == 3 162 for uninit in v: 163 uninit.write('content') 164 165 initialized = uninit.assume_init() 166 ``` 167 """ 168 return tuple(cls() for _ in range(n)) 169 170 @macros.unsafe 171 def assume_init(self) -> T: 172 """Get the inner value, assuming that it was initialized by the caller. 173 174 It is unsafe and undefined behavior to call this method on an uninitialized state, 175 176 Example 177 ------- 178 ```py 179 uninit: MaybeUninit[int] = MaybeUninit.uninit() 180 val = uninit.assume_init() # This is UNSAFE ⚠️ 181 182 # Initialize it first. 183 uninit.write(0) 184 val = uninit.assume_init() # This is safe to access. 185 ``` 186 """ 187 # SAFETY: the caller must guarantee that `self` is initialized. 188 return self.__value 189 190 @staticmethod 191 @macros.unsafe 192 def array_assume_init( 193 array: collections.Sequence[MaybeUninit[T]], 194 ) -> collections.Sequence[T]: 195 """Extracts a sequence of `MaybeUninit[T]` containers. 196 197 You must guarantee that all elements in the array are initialized. 198 199 Example 200 ------- 201 ```py 202 array: list[MaybeUninit[int]] = [MaybeUninit(), MaybeUninit(), MaybeUninit()] 203 array[0].write(0) 204 array[1].write(1) 205 array[2].write(2) 206 # transposed into a tuple. 207 assert MaybeUninit.array_assume_init(array) == (0, 1, 2) 208 ``` 209 """ 210 # SAFETY: The caller guarantees that all elements of the array are initialized 211 return tuple(uninit.assume_init() for uninit in array) 212 213 @staticmethod 214 @macros.unsafe 215 def array_assume_init_mut( 216 array: collections.Sequence[MaybeUninit[T]], 217 ) -> collections.MutableSequence[T]: 218 """Extracts a sequence of `MaybeUninit[T]` to a list of `T`s. 219 220 You must guarantee that all elements in the array are initialized. 221 222 Example 223 ------- 224 ```py 225 array: list[MaybeUninit[int]] = [MaybeUninit(), MaybeUninit(), MaybeUninit()] 226 array[0].write(0) 227 array[1].write(1) 228 array[2].write(2) 229 # transposed into a list. 230 assert MaybeUninit.array_assume_init_mut(array) == [0, 1, 2] 231 ``` 232 """ 233 # SAFETY: The caller guarantees that all elements of the array are initialized 234 return [uninit.assume_init() for uninit in array] 235 236 def write(self, value: T) -> T: 237 """Sets the value of the `MaybeUninit[T]`, Then return a reference to it. 238 239 This will overwrite any previous values, if was initialized. 240 241 Example 242 ------- 243 ```py 244 buffer: MaybeUninit[bytes] = MaybeUninit() 245 buffer.write(b"Hello, World!") 246 print(buffer.assume_init()) 247 ``` 248 """ 249 self.__value = value 250 # SAFETY: we just initialized the value, so it's safe to access. 251 return self.__value 252 253 def __repr__(self) -> str: 254 if self: 255 return f"MaybeUninit(value: {self.__value!r})" 256 return "<uninit>" 257 258 __str__ = __repr__ 259 260 def __bool__(self) -> bool: 261 """Perform a boolean check on whether the value is initialized or not. 262 263 Example 264 ------- 265 ```py 266 v = MaybeUninit[bool]() 267 assert not v 268 ``` 269 """ 270 return hasattr(self, "_MaybeUninit__value") 271 272 def __hash__(self) -> int: 273 return self.__value.__hash__()
A wrapper type to construct uninitialized instances of T.
This is kinda equivalent to Rust's MaybeUninit<T> wrapper.
The difference is what's happening under the hood is when initializing an instance of this object,
nothing really is being initialized, until you call .write() to initialize the inner value.
What??
Ok, so MaybeUninit<T> is in a sense the unsafe equivalent of Option<T>, it either contains a value of type T,
or it contains uninitialized memory (the attribute doesn't exist).
However, MaybeUninit is unable to tell you whether the value it contains is Some(T) or None, So you
as a programmer is responsible for initializing it.
And by default, Option<T> always contain a default value, which is None.
Examples
# Let's write a simple wrapper around `MaybeUninit<T>` to create a dynamic array.
class SizedArray[T]:
def __init__(self, size: int) -> None:
# Creates count `size` of `MaybeUninit<T>` in an uninitialized state.
self.buf = MaybeUninit[T].uninit_array(size)
# Track how many initialized elements are in the array.
self.len = 0
def push(self, value: T) -> None:
# if the length of the array is equal to the size of the array,
# then we've reached the max size of the array.
if self.len == len(self.buf):
raise RuntimeError("max size reached")
# get the next uninitialized element and write into it.
self.buf[self.len].write(value)
self.len += 1
def copy_from(self, other: Sequence[T]) -> None:
# get the remaining space in the array.
spared = self.buf[self.len:]
other_len = len(other)
# Check if there's enough capacity to copy the other list into the array.
if len(spared) < other_len:
raise RuntimeError("not enough space")
# Copy the other list into the array.
index = 0
while index < other_len:
self.buf[self.len + index].write(other[index])
index += 1
self.len += other_len
def read(self) -> Sequence[T]:
return MaybeUninit.array_assume_init(self.buf[:self.len])
array = SizedArray[int](3)
array.push(1)
array.copy_from([2, 3])
assert array.read() == (1, 2, 3)
Implementations
This class implements MaybeUninit in Rust.
133 def __init__(self, value: T | None = None) -> None: 134 if value is None: 135 return None 136 else: 137 # we simply pre-initialize the value if it was passed 138 # when constructing the instance. 139 self.__value = value
Creates a new MaybeUninit
Example
v = MaybeUninit(1)
assert v.assume_init() == 1
141 @classmethod 142 def uninit(cls) -> Self: 143 """Creates a new `MaybeUninit<T>` in an uninitialized state. 144 145 Example 146 ------- 147 ```py 148 v: MaybeUninit[str] = MaybeUninit.uninit() # or just MaybeUninit() 149 ``` 150 """ 151 return cls()
Creates a new MaybeUninit<T> in an uninitialized state.
Example
v: MaybeUninit[str] = MaybeUninit.uninit() # or just MaybeUninit()
153 @classmethod 154 def uninit_array(cls, n: int) -> collections.Sequence[Self]: 155 """Creates an immutable sequence of `MaybeUninit<T>` in an uninitialized state. 156 157 Example 158 ------- 159 ```py 160 v = MaybeUninit[str].uninit_array(3) 161 assert len(v) == 3 162 for uninit in v: 163 uninit.write('content') 164 165 initialized = uninit.assume_init() 166 ``` 167 """ 168 return tuple(cls() for _ in range(n))
Creates an immutable sequence of MaybeUninit<T> in an uninitialized state.
Example
v = MaybeUninit[str].uninit_array(3)
assert len(v) == 3
for uninit in v:
uninit.write('content')
initialized = uninit.assume_init()
170 @macros.unsafe 171 def assume_init(self) -> T: 172 """Get the inner value, assuming that it was initialized by the caller. 173 174 It is unsafe and undefined behavior to call this method on an uninitialized state, 175 176 Example 177 ------- 178 ```py 179 uninit: MaybeUninit[int] = MaybeUninit.uninit() 180 val = uninit.assume_init() # This is UNSAFE ⚠️ 181 182 # Initialize it first. 183 uninit.write(0) 184 val = uninit.assume_init() # This is safe to access. 185 ``` 186 """ 187 # SAFETY: the caller must guarantee that `self` is initialized. 188 return self.__value
Get the inner value, assuming that it was initialized by the caller.
It is unsafe and undefined behavior to call this method on an uninitialized state,
Example
uninit: MaybeUninit[int] = MaybeUninit.uninit()
val = uninit.assume_init() # This is UNSAFE ⚠️
# Initialize it first.
uninit.write(0)
val = uninit.assume_init() # This is safe to access.
Safety ⚠️
Calling this method without knowing the output is considered undefined behavior.
190 @staticmethod 191 @macros.unsafe 192 def array_assume_init( 193 array: collections.Sequence[MaybeUninit[T]], 194 ) -> collections.Sequence[T]: 195 """Extracts a sequence of `MaybeUninit[T]` containers. 196 197 You must guarantee that all elements in the array are initialized. 198 199 Example 200 ------- 201 ```py 202 array: list[MaybeUninit[int]] = [MaybeUninit(), MaybeUninit(), MaybeUninit()] 203 array[0].write(0) 204 array[1].write(1) 205 array[2].write(2) 206 # transposed into a tuple. 207 assert MaybeUninit.array_assume_init(array) == (0, 1, 2) 208 ``` 209 """ 210 # SAFETY: The caller guarantees that all elements of the array are initialized 211 return tuple(uninit.assume_init() for uninit in array)
Extracts a sequence of MaybeUninit[T] containers.
You must guarantee that all elements in the array are initialized.
Example
array: list[MaybeUninit[int]] = [MaybeUninit(), MaybeUninit(), MaybeUninit()]
array[0].write(0)
array[1].write(1)
array[2].write(2)
# transposed into a tuple.
assert MaybeUninit.array_assume_init(array) == (0, 1, 2)
Safety ⚠️
Calling this method without knowing the output is considered undefined behavior.
213 @staticmethod 214 @macros.unsafe 215 def array_assume_init_mut( 216 array: collections.Sequence[MaybeUninit[T]], 217 ) -> collections.MutableSequence[T]: 218 """Extracts a sequence of `MaybeUninit[T]` to a list of `T`s. 219 220 You must guarantee that all elements in the array are initialized. 221 222 Example 223 ------- 224 ```py 225 array: list[MaybeUninit[int]] = [MaybeUninit(), MaybeUninit(), MaybeUninit()] 226 array[0].write(0) 227 array[1].write(1) 228 array[2].write(2) 229 # transposed into a list. 230 assert MaybeUninit.array_assume_init_mut(array) == [0, 1, 2] 231 ``` 232 """ 233 # SAFETY: The caller guarantees that all elements of the array are initialized 234 return [uninit.assume_init() for uninit in array]
Extracts a sequence of MaybeUninit[T] to a list of Ts.
You must guarantee that all elements in the array are initialized.
Example
array: list[MaybeUninit[int]] = [MaybeUninit(), MaybeUninit(), MaybeUninit()]
array[0].write(0)
array[1].write(1)
array[2].write(2)
# transposed into a list.
assert MaybeUninit.array_assume_init_mut(array) == [0, 1, 2]
Safety ⚠️
Calling this method without knowing the output is considered undefined behavior.
236 def write(self, value: T) -> T: 237 """Sets the value of the `MaybeUninit[T]`, Then return a reference to it. 238 239 This will overwrite any previous values, if was initialized. 240 241 Example 242 ------- 243 ```py 244 buffer: MaybeUninit[bytes] = MaybeUninit() 245 buffer.write(b"Hello, World!") 246 print(buffer.assume_init()) 247 ``` 248 """ 249 self.__value = value 250 # SAFETY: we just initialized the value, so it's safe to access. 251 return self.__value
Sets the value of the MaybeUninit[T], Then return a reference to it.
This will overwrite any previous values, if was initialized.
Example
buffer: MaybeUninit[bytes] = MaybeUninit()
buffer.write(b"Hello, World!")
print(buffer.assume_init())