sain.convert API documentation

# sain.convert

Protocols for conversions between types.

The protocols in this module provide a way to convert from one type to another type. Each trait serves a different purpose:

- Implement the From trait for consuming value-to-value conversions
- Implement the Into trait for consuming value-to-value conversions to types outside the current crate
- The TryFrom and TryInto traits behave like From and Into, but should be implemented when the conversion can fail.
- Implement the `ToString` trait for explicitly converting objects to string.

###### Example

    @dataclass
    class Id(From[UUID], Into[int]):
        id: int | float

        @classmethod
        def from_value(cls, value: UUID) -> Self:
            # Keep in mind, this stores a 128 bit <long> integer.
            return cls(int(value))

        def into(self) -> int:
            return int(self.id)

    # Simply perform conversions.
    from_uuid = Id.from_value(uuid4())
    into_int = from_uuid.into()

For type conversions that may fail, two safe interfaces, `TryInto` and `TryFrom` exist which deal with that.

This is useful when you are doing a type conversion that may trivially succeed but may also need special handling.

    @dataclass
    class Message(Into[bytes], TryFrom[bytes, None]):
        content: str
        id: int

        def into(self) -> bytes:
            return json.dumps(self.__dict__).encode()

        @classmethod
        def try_from(cls, value: bytes) -> Result[Self, None]:
            try:
                payload = json.loads(value)
                return Ok(cls(content=payload['content'], id=payload['id']))
            except (json.decoder.JSONDecodeError, KeyError):
                # Its rare to see a JSONDecodeError raised, but usually
                # keys goes missing, which raises a KeyError.
                return Err(None)

    message_bytes = b'{"content": "content", "id": 0}'

    match Message.try_from(message_bytes):
        case Ok(message):
            print("Successful conversion", message)
        case Err(invalid_bytes):
            print("Invalid bytes:", invalid_bytes)

    payload = Message(content='content', id=0)
    assert payload.into() == message_bytes

View Source

      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
     31"""
     32Protocols for conversions between types.
     33
     34The protocols in this module provide a way to convert from one type to another type. Each trait serves a different purpose:
     35
     36* Implement the From trait for consuming value-to-value conversions
     37* Implement the Into trait for consuming value-to-value conversions to types outside the current crate
     38* The TryFrom and TryInto traits behave like From and Into, but should be implemented when the conversion can fail.
     39* Implement the `ToString` trait for explicitly converting objects to string.
     40
     41Example
     42--------
     43```py
     44@dataclass
     45class Id(From[UUID], Into[int]):
     46    id: int | float
     47
     48    @classmethod
     49    def from_value(cls, value: UUID) -> Self:
     50        # Keep in mind, this stores a 128 bit <long> integer.
     51        return cls(int(value))
     52
     53    def into(self) -> int:
     54        return int(self.id)
     55
     56# Simply perform conversions.
     57from_uuid = Id.from_value(uuid4())
     58into_int = from_uuid.into()
     59```
     60
     61For type conversions that may fail, two safe interfaces, `TryInto` and `TryFrom` exist which deal with that.
     62
     63This is useful when you are doing a type conversion that may trivially succeed but may also need special handling.
     64
     65```py
     66@dataclass
     67class Message(Into[bytes], TryFrom[bytes, None]):
     68    content: str
     69    id: int
     70
     71    def into(self) -> bytes:
     72        return json.dumps(self.__dict__).encode()
     73
     74    @classmethod
     75    def try_from(cls, value: bytes) -> Result[Self, None]:
     76        try:
     77            payload = json.loads(value)
     78            return Ok(cls(content=payload['content'], id=payload['id']))
     79        except (json.decoder.JSONDecodeError, KeyError):
     80            # Its rare to see a JSONDecodeError raised, but usually
     81            # keys goes missing, which raises a KeyError.
     82            return Err(None)
     83
     84message_bytes = b'{"content": "content", "id": 0}'
     85
     86match Message.try_from(message_bytes):
     87    case Ok(message):
     88        print("Successful conversion", message)
     89    case Err(invalid_bytes):
     90        print("Invalid bytes:", invalid_bytes)
     91
     92payload = Message(content='content', id=0)
     93assert payload.into() == message_bytes
     94```
     95"""
     96
     97from __future__ import annotations
     98
     99__slots__ = ("Into", "TryInto", "From", "TryFrom", "ToString")
    100
    101import typing
    102
    103from sain.macros import rustc_diagnostic_item
    104
    105if typing.TYPE_CHECKING:
    106    from typing_extensions import Self
    107
    108    from sain import Result
    109
    110T = typing.TypeVar("T")
    111T_co = typing.TypeVar("T_co", contravariant=True)
    112T_cov = typing.TypeVar("T_cov", covariant=True)
    113E = typing.TypeVar("E")
    114
    115
    116@rustc_diagnostic_item("convert_identity")
    117def identity(x: T) -> T:
    118    """The identity function.
    119
    120    This is a function that returns the same value as the input.
    121
    122    While it might seem strange to have a function that just returns back the input, there are some interesting uses.
    123
    124    Example
    125    -------
    126    Using `identity` to do nothing in a sequence of operations.
    127    ```py
    128    from sain.convert import identity
    129
    130    def mangle(x: int) -> int:
    131        return x + 1
    132
    133    arr = [identity, mangle]
    134    ```
    135
    136    Using `identity` to do nothing conditionally.
    137    ```py
    138    from sain.convert import identity
    139
    140    do_stuff = identity if condition else mangle
    141    results = do_stuff(5)
    142    ```
    143    """
    144    return x
    145
    146
    147@rustc_diagnostic_item("From")
    148@typing.runtime_checkable
    149class From(typing.Protocol[T_co]):
    150    """Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into.
    151
    152    As the Rust documentation says, One should always prefer implementing From over Into
    153    because implementing From automatically provides one with an implementation of Into.
    154
    155    But there's no such thing in Python, as it's impossible to auto-impl `Into<T>` for all types
    156    that impl `From<T>`.
    157
    158    So for the sake of simplicity, You should implement whichever interface you want deal with,
    159    Or simply, implement both as the same time.
    160
    161    Example
    162    -------
    163    ```py
    164    @dataclass
    165    class Id(From[str]):
    166        value: int
    167
    168        @classmethod
    169        def from_value(cls, value: str) -> Self:
    170            return cls(value=int(value))
    171
    172    ```
    173    """
    174
    175    __slots__ = ()
    176
    177    @classmethod
    178    def from_value(cls, value: T_co) -> Self:
    179        """Perform the conversion."""
    180        raise NotImplementedError
    181
    182
    183@rustc_diagnostic_item("TryFrom")
    184@typing.runtime_checkable
    185class TryFrom(typing.Protocol[T_co, E]):
    186    """Simple and safe type conversions that may fail in a controlled way under some circumstances.
    187    It is the reciprocal of `TryInto`.
    188
    189    It is useful to implement this when you know that the conversion may fail in some way.
    190
    191    Generic Implementations
    192    -------------------
    193    This interface takes two type arguments, and return `Result<Self, E>`
    194
    195    * `T`: Which's the first generic `T` is the type that's being converted from.
    196    * `E`: If the conversion fails in a way, this is what will return as the error.
    197    * `Self`: Which's the instance of the class that is being converted into.
    198
    199    Example
    200    -------
    201    ```py
    202    @dataclass
    203    class Id(TryFrom[str, str]):
    204        value: int
    205
    206        @classmethod
    207        def try_from(cls, value: str) -> Result[Self, str]:
    208            if not value.isnumeric():
    209                # NaN
    210                return Err(f"Couldn't convert: {value} to self")
    211            # otherwise convert it to an Id instance.
    212            return Ok(value=cls(int(value)))
    213    ```
    214    """
    215
    216    __slots__ = ()
    217
    218    @classmethod
    219    def try_from(cls, value: T_co) -> Result[Self, E]:
    220        """Perform the conversion."""
    221        raise NotImplementedError
    222
    223
    224@rustc_diagnostic_item("TryFrom")
    225@typing.runtime_checkable
    226class Into(typing.Protocol[T_cov]):
    227    """Conversion from `self`, which may or may not be expensive.
    228
    229    Example
    230    -------
    231    ```py
    232    @dataclass
    233    class Id(Into[str]):
    234        value: int
    235
    236        def into(self) -> str:
    237            return str(self.value)
    238    ```
    239    """
    240
    241    __slots__ = ()
    242
    243    def into(self) -> T_cov:
    244        """Perform the conversion."""
    245        raise NotImplementedError
    246
    247
    248@rustc_diagnostic_item("TryInto")
    249@typing.runtime_checkable
    250class TryInto(typing.Protocol[T, E]):
    251    """An attempted conversion from `self`, which may or may not be expensive.
    252
    253    It is useful to implement this when you know that the conversion may fail in some way.
    254
    255    Generic Implementations
    256    -------------------
    257    This interface takes two type arguments, and return `Result<T, E>`
    258
    259    * `T`: The first generic `T` is the type that's being converted into.
    260    * `E`: If the conversion fails in a way, this is what will return as the error.
    261
    262    Example
    263    -------
    264    ```py
    265    @dataclass
    266    class Id(TryInto[int, str]):
    267        value: str
    268
    269        def try_into(self) -> Result[int, str]:
    270            if not self.value.isnumeric():
    271                return Err(f"{self.value} is not a number...")
    272            return Ok(int(self.value))
    273    ```
    274    """
    275
    276    __slots__ = ()
    277
    278    def try_into(self) -> Result[T, E]:
    279        """Perform the conversion."""
    280        raise NotImplementedError
    281
    282
    283@rustc_diagnostic_item("ToString")
    284@typing.runtime_checkable
    285class ToString(typing.Protocol):
    286    """A trait for explicitly converting a value to a `str`.
    287
    288    Example
    289    -------
    290    ```py
    291    class Value[T: bytes](ToString):
    292        buffer: T
    293
    294        def to_string(self) -> str:
    295            return self.buffer.decode("utf-8")
    296    ```
    297    """
    298
    299    __slots__ = ()
    300
    301    def to_string(self) -> str:
    302        """Converts the given value to a `str`.
    303
    304        Example
    305        --------
    306        ```py
    307        i = 5  # assume `int` implements `ToString`
    308        five = "5"
    309        assert five == i.to_string()
    310        ```
    311        """
    312        raise NotImplementedError
    313
    314    def __str__(self) -> str:
    315        return self.to_string()

@rustc_diagnostic_item('convert_identity')
def identity(x: ~T) -> ~T: View Source

    117@rustc_diagnostic_item("convert_identity")
    118def identity(x: T) -> T:
    119    """The identity function.
    120
    121    This is a function that returns the same value as the input.
    122
    123    While it might seem strange to have a function that just returns back the input, there are some interesting uses.
    124
    125    Example
    126    -------
    127    Using `identity` to do nothing in a sequence of operations.
    128    ```py
    129    from sain.convert import identity
    130
    131    def mangle(x: int) -> int:
    132        return x + 1
    133
    134    arr = [identity, mangle]
    135    ```
    136
    137    Using `identity` to do nothing conditionally.
    138    ```py
    139    from sain.convert import identity
    140
    141    do_stuff = identity if condition else mangle
    142    results = do_stuff(5)
    143    ```
    144    """
    145    return x

The identity function.

This is a function that returns the same value as the input.

While it might seem strange to have a function that just returns back the input, there are some interesting uses.

###### Example

Using `identity` to do nothing in a sequence of operations.

    from sain.convert import identity

    def mangle(x: int) -> int:
        return x + 1

    arr = [identity, mangle]

Using `identity` to do nothing conditionally.

    from sain.convert import identity

    do_stuff = identity if condition else mangle
    results = do_stuff(5)

# Implementations

This function implements convert_identity in Rust.

@rustc_diagnostic_item('From')
@typing.runtime_checkable
class From(typing.Protocol[-T_co]): View Source

    148@rustc_diagnostic_item("From")
    149@typing.runtime_checkable
    150class From(typing.Protocol[T_co]):
    151    """Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into.
    152
    153    As the Rust documentation says, One should always prefer implementing From over Into
    154    because implementing From automatically provides one with an implementation of Into.
    155
    156    But there's no such thing in Python, as it's impossible to auto-impl `Into<T>` for all types
    157    that impl `From<T>`.
    158
    159    So for the sake of simplicity, You should implement whichever interface you want deal with,
    160    Or simply, implement both as the same time.
    161
    162    Example
    163    -------
    164    ```py
    165    @dataclass
    166    class Id(From[str]):
    167        value: int
    168
    169        @classmethod
    170        def from_value(cls, value: str) -> Self:
    171            return cls(value=int(value))
    172
    173    ```
    174    """
    175
    176    __slots__ = ()
    177
    178    @classmethod
    179    def from_value(cls, value: T_co) -> Self:
    180        """Perform the conversion."""
    181        raise NotImplementedError

Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into.

As the Rust documentation says, One should always prefer implementing From over Into because implementing From automatically provides one with an implementation of Into.

But there's no such thing in Python, as it's impossible to auto-impl `Into<T>` for all types that impl `From<T>`.

So for the sake of simplicity, You should implement whichever interface you want deal with, Or simply, implement both as the same time.

###### Example

    @dataclass
    class Id(From[str]):
        value: int

        @classmethod
        def from_value(cls, value: str) -> Self:
            return cls(value=int(value))

# Implementations

This class implements From in Rust.

From(*args, **kwargs) View Source

    1941def _no_init_or_replace_init(self, *args, **kwargs):
    1942    cls = type(self)
    1943
    1944    if cls._is_protocol:
    1945        raise TypeError('Protocols cannot be instantiated')
    1946
    1947    # Already using a custom `__init__`. No need to calculate correct
    1948    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
    1949    if cls.__init__ is not _no_init_or_replace_init:
    1950        return
    1951
    1952    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
    1953    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
    1954    # searches for a proper new `__init__` in the MRO. The new `__init__`
    1955    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
    1956    # instantiation of the protocol subclass will thus use the new
    1957    # `__init__` and no longer call `_no_init_or_replace_init`.
    1958    for base in cls.__mro__:
    1959        init = base.__dict__.get('__init__', _no_init_or_replace_init)
    1960        if init is not _no_init_or_replace_init:
    1961            cls.__init__ = init
    1962            break
    1963    else:
    1964        # should not happen
    1965        cls.__init__ = object.__init__
    1966
    1967    cls.__init__(self, *args, **kwargs)

@classmethod
def from_value(cls, value: -T_co) -> Self: View Source

    178    @classmethod
    179    def from_value(cls, value: T_co) -> Self:
    180        """Perform the conversion."""
    181        raise NotImplementedError

Perform the conversion.

@rustc_diagnostic_item('TryFrom')
@typing.runtime_checkable
class TryFrom(typing.Protocol[-T_co, ~E]): View Source

    184@rustc_diagnostic_item("TryFrom")
    185@typing.runtime_checkable
    186class TryFrom(typing.Protocol[T_co, E]):
    187    """Simple and safe type conversions that may fail in a controlled way under some circumstances.
    188    It is the reciprocal of `TryInto`.
    189
    190    It is useful to implement this when you know that the conversion may fail in some way.
    191
    192    Generic Implementations
    193    -------------------
    194    This interface takes two type arguments, and return `Result<Self, E>`
    195
    196    * `T`: Which's the first generic `T` is the type that's being converted from.
    197    * `E`: If the conversion fails in a way, this is what will return as the error.
    198    * `Self`: Which's the instance of the class that is being converted into.
    199
    200    Example
    201    -------
    202    ```py
    203    @dataclass
    204    class Id(TryFrom[str, str]):
    205        value: int
    206
    207        @classmethod
    208        def try_from(cls, value: str) -> Result[Self, str]:
    209            if not value.isnumeric():
    210                # NaN
    211                return Err(f"Couldn't convert: {value} to self")
    212            # otherwise convert it to an Id instance.
    213            return Ok(value=cls(int(value)))
    214    ```
    215    """
    216
    217    __slots__ = ()
    218
    219    @classmethod
    220    def try_from(cls, value: T_co) -> Result[Self, E]:
    221        """Perform the conversion."""
    222        raise NotImplementedError

Simple and safe type conversions that may fail in a controlled way under some circumstances. It is the reciprocal of `TryInto`.

It is useful to implement this when you know that the conversion may fail in some way.

###### Generic Implementations

This interface takes two type arguments, and return `Result<Self, E>`

- `T`: Which's the first generic `T` is the type that's being converted from.
- `E`: If the conversion fails in a way, this is what will return as the error.
- `Self`: Which's the instance of the class that is being converted into.

###### Example

    @dataclass
    class Id(TryFrom[str, str]):
        value: int

        @classmethod
        def try_from(cls, value: str) -> Result[Self, str]:
            if not value.isnumeric():
                # NaN
                return Err(f"Couldn't convert: {value} to self")
            # otherwise convert it to an Id instance.
            return Ok(value=cls(int(value)))

# Implementations

This class implements TryFrom in Rust.

TryFrom(*args, **kwargs) View Source

    1941def _no_init_or_replace_init(self, *args, **kwargs):
    1942    cls = type(self)
    1943
    1944    if cls._is_protocol:
    1945        raise TypeError('Protocols cannot be instantiated')
    1946
    1947    # Already using a custom `__init__`. No need to calculate correct
    1948    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
    1949    if cls.__init__ is not _no_init_or_replace_init:
    1950        return
    1951
    1952    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
    1953    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
    1954    # searches for a proper new `__init__` in the MRO. The new `__init__`
    1955    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
    1956    # instantiation of the protocol subclass will thus use the new
    1957    # `__init__` and no longer call `_no_init_or_replace_init`.
    1958    for base in cls.__mro__:
    1959        init = base.__dict__.get('__init__', _no_init_or_replace_init)
    1960        if init is not _no_init_or_replace_init:
    1961            cls.__init__ = init
    1962            break
    1963    else:
    1964        # should not happen
    1965        cls.__init__ = object.__init__
    1966
    1967    cls.__init__(self, *args, **kwargs)

@classmethod
def try_from(cls, value: -T_co) -> 'Result[Self, E]': View Source

    219    @classmethod
    220    def try_from(cls, value: T_co) -> Result[Self, E]:
    221        """Perform the conversion."""
    222        raise NotImplementedError

Perform the conversion.

@rustc_diagnostic_item('TryFrom')
@typing.runtime_checkable
class Into(typing.Protocol[+T_cov]): View Source

    225@rustc_diagnostic_item("TryFrom")
    226@typing.runtime_checkable
    227class Into(typing.Protocol[T_cov]):
    228    """Conversion from `self`, which may or may not be expensive.
    229
    230    Example
    231    -------
    232    ```py
    233    @dataclass
    234    class Id(Into[str]):
    235        value: int
    236
    237        def into(self) -> str:
    238            return str(self.value)
    239    ```
    240    """
    241
    242    __slots__ = ()
    243
    244    def into(self) -> T_cov:
    245        """Perform the conversion."""
    246        raise NotImplementedError

Conversion from `self`, which may or may not be expensive.

###### Example

    @dataclass
    class Id(Into[str]):
        value: int

        def into(self) -> str:
            return str(self.value)

# Implementations

This class implements TryFrom in Rust.

Into(*args, **kwargs) View Source

    1941def _no_init_or_replace_init(self, *args, **kwargs):
    1942    cls = type(self)
    1943
    1944    if cls._is_protocol:
    1945        raise TypeError('Protocols cannot be instantiated')
    1946
    1947    # Already using a custom `__init__`. No need to calculate correct
    1948    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
    1949    if cls.__init__ is not _no_init_or_replace_init:
    1950        return
    1951
    1952    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
    1953    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
    1954    # searches for a proper new `__init__` in the MRO. The new `__init__`
    1955    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
    1956    # instantiation of the protocol subclass will thus use the new
    1957    # `__init__` and no longer call `_no_init_or_replace_init`.
    1958    for base in cls.__mro__:
    1959        init = base.__dict__.get('__init__', _no_init_or_replace_init)
    1960        if init is not _no_init_or_replace_init:
    1961            cls.__init__ = init
    1962            break
    1963    else:
    1964        # should not happen
    1965        cls.__init__ = object.__init__
    1966
    1967    cls.__init__(self, *args, **kwargs)

def into(self) -> +T_cov: View Source

    244    def into(self) -> T_cov:
    245        """Perform the conversion."""
    246        raise NotImplementedError

Perform the conversion.

@rustc_diagnostic_item('TryInto')
@typing.runtime_checkable
class TryInto(typing.Protocol[~T, ~E]): View Source

    249@rustc_diagnostic_item("TryInto")
    250@typing.runtime_checkable
    251class TryInto(typing.Protocol[T, E]):
    252    """An attempted conversion from `self`, which may or may not be expensive.
    253
    254    It is useful to implement this when you know that the conversion may fail in some way.
    255
    256    Generic Implementations
    257    -------------------
    258    This interface takes two type arguments, and return `Result<T, E>`
    259
    260    * `T`: The first generic `T` is the type that's being converted into.
    261    * `E`: If the conversion fails in a way, this is what will return as the error.
    262
    263    Example
    264    -------
    265    ```py
    266    @dataclass
    267    class Id(TryInto[int, str]):
    268        value: str
    269
    270        def try_into(self) -> Result[int, str]:
    271            if not self.value.isnumeric():
    272                return Err(f"{self.value} is not a number...")
    273            return Ok(int(self.value))
    274    ```
    275    """
    276
    277    __slots__ = ()
    278
    279    def try_into(self) -> Result[T, E]:
    280        """Perform the conversion."""
    281        raise NotImplementedError

An attempted conversion from `self`, which may or may not be expensive.

It is useful to implement this when you know that the conversion may fail in some way.

###### Generic Implementations

This interface takes two type arguments, and return `Result<T, E>`

- `T`: The first generic `T` is the type that's being converted into.
- `E`: If the conversion fails in a way, this is what will return as the error.

###### Example

    @dataclass
    class Id(TryInto[int, str]):
        value: str

        def try_into(self) -> Result[int, str]:
            if not self.value.isnumeric():
                return Err(f"{self.value} is not a number...")
            return Ok(int(self.value))

# Implementations

This class implements TryInto in Rust.

TryInto(*args, **kwargs) View Source

    1941def _no_init_or_replace_init(self, *args, **kwargs):
    1942    cls = type(self)
    1943
    1944    if cls._is_protocol:
    1945        raise TypeError('Protocols cannot be instantiated')
    1946
    1947    # Already using a custom `__init__`. No need to calculate correct
    1948    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
    1949    if cls.__init__ is not _no_init_or_replace_init:
    1950        return
    1951
    1952    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
    1953    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
    1954    # searches for a proper new `__init__` in the MRO. The new `__init__`
    1955    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
    1956    # instantiation of the protocol subclass will thus use the new
    1957    # `__init__` and no longer call `_no_init_or_replace_init`.
    1958    for base in cls.__mro__:
    1959        init = base.__dict__.get('__init__', _no_init_or_replace_init)
    1960        if init is not _no_init_or_replace_init:
    1961            cls.__init__ = init
    1962            break
    1963    else:
    1964        # should not happen
    1965        cls.__init__ = object.__init__
    1966
    1967    cls.__init__(self, *args, **kwargs)

def try_into(self) -> 'Result[T, E]': View Source

    279    def try_into(self) -> Result[T, E]:
    280        """Perform the conversion."""
    281        raise NotImplementedError

Perform the conversion.

@rustc_diagnostic_item('ToString')
@typing.runtime_checkable
class ToString(typing.Protocol): View Source

    284@rustc_diagnostic_item("ToString")
    285@typing.runtime_checkable
    286class ToString(typing.Protocol):
    287    """A trait for explicitly converting a value to a `str`.
    288
    289    Example
    290    -------
    291    ```py
    292    class Value[T: bytes](ToString):
    293        buffer: T
    294
    295        def to_string(self) -> str:
    296            return self.buffer.decode("utf-8")
    297    ```
    298    """
    299
    300    __slots__ = ()
    301
    302    def to_string(self) -> str:
    303        """Converts the given value to a `str`.
    304
    305        Example
    306        --------
    307        ```py
    308        i = 5  # assume `int` implements `ToString`
    309        five = "5"
    310        assert five == i.to_string()
    311        ```
    312        """
    313        raise NotImplementedError
    314
    315    def __str__(self) -> str:
    316        return self.to_string()

A trait for explicitly converting a value to a `str`.

###### Example

    class Value[T: bytes](ToString):
        buffer: T

        def to_string(self) -> str:
            return self.buffer.decode("utf-8")

# Implementations

This class implements ToString in Rust.

ToString(*args, **kwargs) View Source

    1941def _no_init_or_replace_init(self, *args, **kwargs):
    1942    cls = type(self)
    1943
    1944    if cls._is_protocol:
    1945        raise TypeError('Protocols cannot be instantiated')
    1946
    1947    # Already using a custom `__init__`. No need to calculate correct
    1948    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
    1949    if cls.__init__ is not _no_init_or_replace_init:
    1950        return
    1951
    1952    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
    1953    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
    1954    # searches for a proper new `__init__` in the MRO. The new `__init__`
    1955    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
    1956    # instantiation of the protocol subclass will thus use the new
    1957    # `__init__` and no longer call `_no_init_or_replace_init`.
    1958    for base in cls.__mro__:
    1959        init = base.__dict__.get('__init__', _no_init_or_replace_init)
    1960        if init is not _no_init_or_replace_init:
    1961            cls.__init__ = init
    1962            break
    1963    else:
    1964        # should not happen
    1965        cls.__init__ = object.__init__
    1966
    1967    cls.__init__(self, *args, **kwargs)

def to_string(self) -> str: View Source

    302    def to_string(self) -> str:
    303        """Converts the given value to a `str`.
    304
    305        Example
    306        --------
    307        ```py
    308        i = 5  # assume `int` implements `ToString`
    309        five = "5"
    310        assert five == i.to_string()
    311        ```
    312        """
    313        raise NotImplementedError

Converts the given value to a `str`.

###### Example

    i = 5  # assume `int` implements `ToString`
    five = "5"
    assert five == i.to_string()