You MUST KNOW These Traits in Rust
Summary
TLDRこの動画スクリプトでは、Rust言語の特徴的なトレイト「From」と「Into」、およびそれらのエラーハンドリングバージョン「TryFrom」と「TryInto」について解説しています。Rustにおけるデータの所有権と借用の概念を理解し、これらのトレイトを使用してAPIの使いやすさを向上させる方法を学ぶことができます。また、データの変換が失敗する可能性がある場合にどのように扱うか、そして参照と借用に関する「AsRef」と「AsMut」トレイトの使い方についても説明されています。この知識は、Rustの標準ライブラリや一般的なクレートのAPIをより理解し、より効率的にコードを書くための重要な基盤となります。
Takeaways
- 🌟 Rustの借用チェッカーを克服すると、コードがよりスムーズに流れるようになります。
- 📚 Rustにおけるidiomaticなコードを学ぶことの重要性と、特定のトレイト(from, into, try_from, try_into)を知ることがAPIの使いやすさに寄与します。
- 🔄 `from`と`into`は、Rustでの型変換の基本であり、`into`は`from`の逆演算です。
- 📊 Rustは所有データと借用データの2つのタイプがあり、`from`と`into`はこれらのデータの変換に関連しています。
- 🚀 `from`トレイトは、変換が失敗しないことを前提としており、計算コストが高い可能性があることを考慮する必要があります。
- 🔧 `try_from`と`try_into`は、失敗が可能な変換を扱うために存在し、関連型`Error`を定義することができます。
- 🛠️ 型変換を標準化することで、Rust開発者が検索しやすくなり、特定の型変換に必要なメソッドを簡単に見つけることができます。
- 📌 関数のジェネリクスと`Into`トレイトの使用により、APIのエルゴノミクスを向上させることができます。
- 🔄 `as_ref`と`as_mut`は、所有権を取得せずに値を借用するためのトレイトで、参照の変換がより簡単に行えます。
- 📝 標準ライブラリは`as_ref`と`as_mut`のブランケット実装を提供しており、異なるタイプのデータの借用を自動的に扱うことができます。
- 🔗 これらのトレイトの理解と使用は、Rustでのコードの改善と、既存のクレートやAPIの理解を深めることに役立ちます。
Q & A
Rustの借用チェッカーとは何ですか?
-Rustの借用チェッカーは、所有データと借用データの管理を担当し、安全なメモリアクセスを保証するコンパイラの機能です。
Rustにおける所有データと借用データの違いは何ですか?
-所有データは、コードブロックがメモリを解放する責任を持っており、借用データはそのメモリの解放を後で他のコードブロックが行うことになります。
FromトレイトとIntoトレイトの違いは何ですか?
-Fromトレイトは変換の所有権を取得する変換を定義し、Intoトレイトは変換の所有権を放棄する変換を定義します。IntoはFromの逆です。
Rustで標準的な変換を行うにはどうすればいいですか?
-Rustで標準的な変換を行うには、FromトレイトとIntoトレイトを実装し、それらを使用して変換を行うことが必要です。
TryFromとTryIntoはどのような状況で使用されるのでしょうか?
-TryFromとTryIntoは、変換が失敗する可能性がある場合に使用されます。これらのトレイトは、エラーを返すことができます。
Rustのトレイトに関連する型がある場合、どのようにエラーを定義するのですか?
-関連する型がある場合、Associated typeを使用してエラーの型を定義することができます。
Rustで借用参照を取得する方法は何ですか?
-Rustで借用参照を取得するには、AsRefトレイトとAsMutトレイトを使用することができます。これらは、参照を取得するためのcheapな方法を提供します。
Rustでファイルの名前を変更する方法は何ですか?
-Rustでファイルの名前を変更するには、std::fsモジュールのrename関数を使用することができます。この関数は、所有権を取得せずにパスを借用します。
Rustで標準ライブラリが提供するAsRefとAsMutの利点は何ですか?
-AsRefとAsMutは、関数に借用参照を渡す際に、コードの可読性と使いやすさを向上させます。また、標準ライブラリは、これらのトレイトのblanket実装を提供し、複数の参照を自動的にdereferenceします。
RustでAPIの使いやすさを向上させるためにどのようなアプローチがとることができますか?
-APIの使いやすさを向上させるために、関数をジェネリックにし、Intoトレイトを利用して、所有権を取得するデータを受け取ることができます。また、AsRefやAsMutを利用して借用参照を渡すこともできます。
Outlines
🤖 Rustのidiomaticコードとtraitsの理解
Rustは初心者には厳しい言語ですが、borrow Checkerを克服した後、コードがスムーズに流れ始めます。この段階でidiomaticなコードとtraitsを学ぶことが重要です。特に、型変換を扱うtraitsである`From`と`Into`、およびエラーを許容する`TryFrom`と`TryInto`について理解を深めましょう。これらのtraitsはAPIの使いやすさとコードの効率を劇的に向上させる効果があります。
🔄 データ型変換の標準化と利点
Rustでは所有データと借用データの概念があり、これに基づいて`From`と`Into`の2つの型変換方法があります。`From`は変換元のデータ型の所有権を取得し、`Into`は変換先のデータ型に変換します。これらのtraitsは標準化された変換方法を提供し、開発者が型変換に必要なメソッドを簡単に検索できるようになります。また、APIの改善や内部データの扱いを簡素化する効果もあります。
🚀 失敗可能な変換と`TryFrom`/`TryInto`
型変換が失敗する可能性がある場合、`TryFrom`と`TryInto`が使用されます。これらのtraitsはエラーを返すことができます。`TryFrom`は変換元の型からエラー型を指定し、`TryInto`は変換先の型が`From`を実装していることを要求します。これらのtraitsを使用することで、失敗時のエラー処理が柔軟になり、コードの信頼性が向上します。
📚 借用参照と`AsRef`/`AsMut`の使用
Rustでは所有権を必要としない場合、借用参照を扱う`AsRef`と`AsMut`のtraitsがあります。`AsRef`は借用参照を返し、`AsMut`は可変借用参照を返します。これらのtraitsを使用することで、APIの使いやすさやコードの効率が向上し、特に深い型のネストがある場合に有効です。標準ライブラリはこれらのtraitsのblanket実装を提供しており、開発者が簡単に実装できるようになっています。
Mindmap
Keywords
💡Rust
💡Borrow Checker
💡From and Into Traits
💡Idiomatic Code
💡TryFrom and TryInto
💡AsRef and AsMut
💡Generic Functions
💡Type Conversion
💡Ownership
💡Ergonomics
💡Associated Types
Highlights
Rust's borrow checker makes the language challenging for beginners but rewarding once mastered.
Understanding idiomatic Rust code involves learning about traits like `From` and `Into`.
Traits `From` and `Into` are essential for type conversion in Rust.
In Rust, data can be owned or borrowed, affecting how conversion traits work.
The `From` trait is used for conversions that transfer ownership.
The `Into` trait is the reciprocal of `From` and is used when the target type implements `From` for the source type.
Using `From` and `Into` traits can improve API ergonomics and make code more idiomatic.
The `TryFrom` and `TryInto` traits handle failable conversions, allowing for error handling.
Associated types in `TryFrom` allow for specifying the error type for conversion failures.
The `as_ref` and `as_mut` traits are used for cheap reference borrowing, improving API usability.
The Rust standard library provides blanket implementations for `as_ref` and `as_mut`, simplifying type handling.
The `rename` function from the standard library demonstrates the use of `as_ref` for non-owning operations.
Borrowing mutable references with `as_mut` allows for modifying data without taking ownership.
The standard library's blanket implementations for mutable references enable passing多层 mutable references.
Understanding and using these traits can lead to more efficient and idiomatic Rust code.
Familiarity with these traits can help developers better understand and utilize crates and APIs in Rust.
The transcript provides a comprehensive overview of Rust's type conversion traits and their practical applications.
Transcripts
rust might not be the most beginner
friendly language but after you've
battled and hopefully defeated the
borrow Checker Things become way easier
and your code slowly begins to flow more
and more this is however also the time
when you really need to learn more about
rust and especially idiomatic code and
there are a few traits that you need to
know about because they will make your
API so much better to use and even ease
your own life up a lot the traits I'm
talking about are from and into try from
and try into and as Raven as mute these
trades are the base of converting
between types and rust and you should
really be aware of and use them actively
so let's take a look at them and find
out how they work why you need them and
where you can use them let's first talk
about from and into because they are two
sides of the same metal and into is
actually just the reciprocal of the from
trade but more on that later conversion
happens all the time in software often
we have one type of data that we need to
convert into another type of data
database results need to be converted
into API responses API requests need to
be converted to an internal
representation or a software can work
with and so on in Rust there are two
types of data there is owned data and
borrowed data and it's one of the core
principles of rust when a code block
owns some data it's also responsible for
freeing the corresponding memory when it
has finished whatever it wanted to do if
if a code block only borrows it it just
has to return the borrow data so to say
and let someone else take care of
freeing that block of memory later as
rust has these two ways of dealing with
data it's also no wonder that there are
at least two ways of converting it from
an into our trades that deal with the
conversion of data we want to get
ownership of which means that after the
conversion happened ownership is passed
from the caller whoever that might be to
us or better our logic this means that
we we decide when the lifetime of that
converted data ends if we take a look at
the implementation of the from trade we
can quickly see what it focuses on it
takes a generic Source type T and
returns itself which refers to whatever
type implements from for itself as the
argument is not borrowed because there
is no ENT in front of it the target type
is moved or in other words consumed and
thus gives ownership to from as the
function itself only returns self it
means that we deal with a conversion
that can't fail from should always
return a result so it's not suited for
conversions that can potentially fail
due to constraints a conversion model
with from can or cannot be
computationally expensive but in general
we should assume that it is expensive
which is important to keep in mind when
we use it next to from there's in two
which is the reciprocal of from and if
we take a look at it we can see that
into takes the type that implements it
and returns a Target Type U which in the
end is just another way of the
conversion from does looking at the
signature we can see that from implies
into the target type is required to
implement from for into Source type and
the blanket implementation of into makes
use of exactly that by only calling from
on the target type passing self as the
argument oh and like with from the
argument self in this case is moved and
thus consumed to make clear that into is
really the reciprocal of from and to
help you understand it let's take a look
at how we can construct a string from an
Str Str in Rust we can either create
that string by calling string from
passing an Str Str into it call from on
the from trade but then we have to
explicitly State our type call into on
the Str Str and explicitly type our
variable string or by calling into on
the into trade also explicitly typing
our variable all lines do exactly the
same in the end we have a dynamic keep
allocated string at our hands created
from an immutable utf8 bite sequence and
Str Str somewhere within our programs
memory which of the ways we use is
usually a matter of taste but we will
mostly find versions 1 and three out in
the wild which is either calling from on
the target type and calling into
directly on the value of the type we
have at hand the standardized way of
converting values is already pretty
valuable on its own if we stick to it
rust devs will know what to search for
and won't need to go and see which
method they might need to use to convert
from one type to another if our type
implements from other devs will know how
to use it okay that was a lot of theory
so let's see how we can effectively use
these two traits besides standardizing
conversions with them imagine that we
have a simple struct that models the
metadata of a YouTube video this struct
has at least two properties a title and
a description both of typ string if we
now want to create a factory function
for that struct we could just let our
users pass two strings to it it's
already clear that we want to take
ownership of that data because well
whatever we put inside the struct needs
to live as long as the struct itself
does but this API doesn't have a lot of
ergonomics if we want to construct an
instance of that struct it only accepts
two strings and that's it we can always
let the users of our API convert their
types themselves like sdrs for example
but that leaves some pretty ugly code
behind for them if we change the
signature of our Factory method to
accepting it into string however the
ergonomics of our Factory suddenly
become way better our Factory is now
generic over a type that must Implement
into string and it doesn't matter which
type that is now the call with an S strr
simply looks like this which is way
better for users of such an API because
they don't have to explicitly convert
between types anymore they can Implement
into string for their types if they wish
to
and then pass them to our function we
can extend this example to anything else
but in general whenever we want to
accept data that we need to take
ownership of we should at least consider
making our function generic and
accepting an in of whatever type we need
because it can improve the ergonomics of
our API doing so also makes sense for
internal types because it allows us to
implement from for some of them exactly
once for each type we need get the into
implementation for free and save
ourselves lot of work when we pass data
of different types around within the
internals of our software until now we
have only talked and learned about
conversions that cannot fail but what if
they can what if they are failable the
signature from and into doesn't allow
for any errors and panics should
generally be avoided because no one
likes software that just shuts down
whenever it hits a small inconvenience
and this is why Try from and try into
exist if we take a look at try from from
source code we can see that it looks a
little more complicated than from but it
actually only has one more advanced
feature inside it a so-called Associated
type arror try from is generic over type
T which means that t is the source and
self is the target type that we
Implement try from for this however
leaves one problem how can we make the
error generic it's not as easy as simply
adding another generic type parameter
which is why the associated type exists
this Associated type is just a simple
placeholder that we can specify when
implementing the trade which allows us
to specify the type of error we want to
return in case the conversion fails
before we look at an example though we
can pay a short wizard to try into
source code and identify that like into
for from try into also has a blanket
implementation which only requires us to
implement Tri from and get try into for
free everything within try into is
direct linked to Tri from even its
Associated type is bound to the one
within Tri from so there's really no
work for us to do unless we have to in
really rare circumstances with that done
let's now look at an example scenario
HTTP and https both work over TCP they
are mainly the foundation of the
internet as it is today with https being
the preferred standard for public
traffic if we were to implement an HTTP
and https server we would have to think
in bite streams that our application
receives and correctly interpret those
bites at the beginning there is a new
connection which any client opens to our
server on its first request that's the
moment we have to determine whether we
are dealing with HTTP or https traffic
and a way to distinguish one from the
other is by looking at exactly the first
bite of the first TCP packet in case of
https that bite will always be 22
because of the TLs handshake that occurs
in the beginning if if the connection is
only through HTTP we receive any bite
between 32 and 127 inclusive a way to
model this is to use an enum with
exactly two members one that signals
that the type of the connection is HTTP
and the other that it's https this e
alone doesn't help us though we deal
with a bite stream which is just a
buffer of btes that are u8 and rust case
and this already screams for some form
of conversion although we are already
talking about the TR versions of from it
into
it's still good to understand why
they're necessary in this case you
probably still remember that we have a
very limited set of acceptable values
for our Ena to be precise we only accept
96 possible values of a u8 that can take
any number from 0 to 255 inclusive this
leaves many bites that don't fit into
our enom and what do we do if we cannot
deal with certain values correct we
return an arrow if we receive one we
can't work with we can model exactly
that by by implementing triy from u8 for
our enum traffic type as our Arrow we
use one that we Define ourselves with
friendly help from this arrow and within
the function we use a match statement
that checks the value we convert from
for all possible States if the bite we
receive is 22 we deal with https traffic
if the bite we receive is in the range
between 32 and 127 inclusive we deal
with HTTP traffic and if we receive
anything else we return on an err with
our self-made P error which signals that
we got an invalid first bite with this
implementation we've created a way to
convert the first bite of a TCP stream
into an enum that tells us what type of
connection we deal with and with that we
can subsequently Implement logic to deal
with each individual type this is of
course not the end of it we can also use
try from and try into as a function
argument the same way we already did
with from and into if we want wanted to
create the Boolean predicate function to
tell us whether we deal with HTP or
https or none of them at all we can do
it by accepting a try into traffic types
or parameter and then base our Logic on
the result of the conversion admittedly
the use cases for Tryin as an argument
are fewer than those for into but
especially in create internal code you
can make good use of it so it's still a
very valid use case up to now we've only
talked about conversions that take
ownership but more often than not we
don't need any ownership to do what we
want to do in fact we can even argue
that the majority of the time borrowing
a value is more than enough this however
rules out using from into try from and
try into for cases like this sref exists
and we can take a quick look at its
source code and try to make a little
sense of it srf is a generic trade with
exactly one function to implement as ref
as ref borrows itself so it doesn't move
itself like the other conversions do and
it returns a reference to a Target type
T in the end s ref is meant for cheap
conversions of references if a type A
contains a type B implementing as ref B
for type A allows us to return the
nested reference of B with an a which is
very cheap to do and further it also
saves users of our apis to pass huge
call chains into our functions like from
into and the others do but let's as an
example look at how we can work with
files in Rust to make it even more
precise let's imagine that we are
dealing with renaming files which is a
common operation whenever we download a
file our browsers tend to create a
temporary file until the download is
finished and rename it to its real name
after this if we want to do the same in
Rust we can use a sweet little helper
method from the standard Library within
the fs module called rename it accepts
two paths the source and the Target and
when it has finished its execution the
source file has the name of the target
we supplied this function doesn't
actually need ownership of the arguments
it only needs the paths to identify the
source file and to get a name and an
absolute path to the Target file so
borrowing makes perfect sense even more
interestingly it accepts more than only
paths we can even mix and match
arguments as we like rename still works
and still only borrows its arguments if
we take a look at rename source code we
can spot how the magic works it makes
use of as ref and requires both of its
parameters to implement as ref path
that's it the actual magic however is
nested within the individual
implementations of his ref for multiple
types within the standard Library there
are quite a few types that Implement as
ref path which is why you can call
rename with many different types of data
the cheap conversion from whatever type
to a path reference happens under the
hood actually even within paths Factory
function which converts any type it
receives to a reference of an OS string
to create a path from let's however look
at another example to get an even better
idea of how SRA is intended to work and
how we can make use of it let's imagine
we build a block system with various
data the most basic form of the data we
need is a post that has a few crucial
properties there are also Advanced types
of post like video guides that we need
to model as rars however lack
inheritance for good reason we usually
model it with the new type principle and
create a new type adding the additional
properties we need and nesting the
original type within it now we want the
function to notify all of our regular
readers that a new post has been
published it needs some metadata from
the post to create an email with which
is why borrowing is enough and then does
some magical work to notify all readers
of the new content passing the
references is perfectly fine and fast
but the deeper post is nested with an
Advanced types the longer the call
chains become calling the post property
on the video guide is fine but it's
already looking a little ugly this is
where we can bring s ref in and
Implement s ref post for all of our
types then we can change the signature
of our notify function to accept any
type that implements as ref post which
then allows us to get a cheap reference
to a post no matter how deeply it is
nested within a random time this also
beautifies the Coster of function
because we can now just pass any type
that implements as Rift post but wait a
second have you noticed something if not
don't worry let's take a closer look
notify does magically work by passing a
reference to a post in a video guide
although we have only implemented as ref
post for post and video guide and not
for reference post and reference video
guide that's actually another blanket
implementation within the standard
Library doing some magic for us this
blanket implementation automatically
gifts us with an implementation of srf
post for both reference post and
reference video guide as soon as we
implement it for the non-reference types
oh and even better the standard Library
even goes one step further and
implements s ref t for us for any
mutable reference which means that we
can also automatically pass mutable
references to any function that expects
an srf T without any compiler errors
that's quite some time saved and it
improves the usability of our API I even
more because now users don't even have
to think about what they need to pass
they can pass us what they have at hand
there is one last thing missing however
borrowing a mutable reference that's
something as ref can't cover which is
why as mute exists a look at its source
code reveals that it's mainly the same
as as ref with the only difference being
that it requires a mutable self and
returns a mutable reference together
with a different function name this
makes us mute the way to go if we don't
want an immutable but a mutable borrow
somewhere with no API its function name
is different from as ref which allows us
to implement both traits and this then
allows us to make explicit calls that
also Mark Our intention s ref if we want
an immutable reference and S mute if we
want a mutable one using the same block
example we already used for S ref we
could also implement the function that
can mutate our post to store the date we
notified our users from example this is
nearly the same as RS ref example just
that we can work with mutable references
this time oh and who would have thought
the standard Library once again gives us
a blanket implementation for mutable
references which AO dereferences
multiple layers of mutable references
for us or in other words even a mutable
reference of a mutable reference of a
type T can be passed as an S mute T and
is automatically dereferenced once again
something that saves us time and makes
apis more usable so overall a win-win
situation pH that was a lot to grasp
take some time to process that first and
after that you will hopefully realize
how much better your code becomes if you
begin to consequently use these trades
in your daily work or your side project
and even if you don't have the time or
opportunities to use them yourselves you
will still profit from that knowledge
because you now probably understand a
few crates and their apis and especially
the standard Library better than you did
before so hey enjoy your newly gained
knowledge and until then see you in the
next video
5.0 / 5 (0 votes)