What is a “fat pointer” in Rust?What are the differences between a pointer variable and a reference variable in C++?What is a smart pointer and when should I use one?What is the difference between const int*, const int * const, and int const *?Typedef function pointer?When to use references vs. pointersWhy should I use a pointer rather than the object itself?What makes something a “trait object”?What's the de-facto way of reading and writing files in Rust 1.x?Pointer to traitWhy do fat pointers sometimes percolate outwards?
Why is k-means used for non normally distributed data?
What can prevent a super elevator to a space station from being invented?
What percentage of the mass/energy of the universe is in the form of electromagnetic waves?
Design of 50 ohms RF trace for 2.4GHz...Double layer FR-4 PCB
Why does dough containing a small amount of terumah become exempt from challah?
Divide Numbers by 0
Meaning of "educating the ice"
Visiting girlfriend in the USA
Inkscape, align objects top has space between objects
When do we use "no women" instead of "no woman"?
Babies - are we talking about their birth, arrival or delivery?
FHE: What is the difference between multiplicative depth and multiplicative level?
Lumix G7: Raw photos only in 1920x1440, no higher res available
How do you manage to study and have a balance in your life at the same time?
Received email from ISP saying one of my devices has malware
Why are Latin and Sanskrit called dead languages?
What is the maximal acceptable delay between pilot's input and flight control surface actuation?
Are manifolds admitting a circle foliation covered by manifolds with a (non-trivial) circle action?
How to fit Schwalbe Marathon Plus 28-622 on 622-16 rim
Can Russians naturally pronounce "попал в бесперспективняк"?
Why would a Intel 8080 chip be destroyed if +12 V is connected before -5 V?
Why can't I summon my friend to help me?
Is torque as fundamental a concept as force?
How to align values in table according to the pm and point?
What is a “fat pointer” in Rust?
What are the differences between a pointer variable and a reference variable in C++?What is a smart pointer and when should I use one?What is the difference between const int*, const int * const, and int const *?Typedef function pointer?When to use references vs. pointersWhy should I use a pointer rather than the object itself?What makes something a “trait object”?What's the de-facto way of reading and writing files in Rust 1.x?Pointer to traitWhy do fat pointers sometimes percolate outwards?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
I've read the term "fat pointer" in several contexts already, but I'm not sure what exactly it means and when it is used in Rust. The pointer seems to be twice as large as a normal pointer, but I don't understand why. It also seems to have something to do with trait objects.
pointers rust
add a comment |
I've read the term "fat pointer" in several contexts already, but I'm not sure what exactly it means and when it is used in Rust. The pointer seems to be twice as large as a normal pointer, but I don't understand why. It also seems to have something to do with trait objects.
pointers rust
Note: I noticed that the Google results for "rust fat pointer" are really not great, i.e. it is not easy to quickly see what a fat pointer is. That's why I created this Q&A.
– Lukas Kalbertodt
11 hours ago
12
I personally think this is a great Q&A.
– BurntSushi5
10 hours ago
1
Thank you for the interesting post. This was very informative for a Rust noob like myself.
– Jman
7 hours ago
add a comment |
I've read the term "fat pointer" in several contexts already, but I'm not sure what exactly it means and when it is used in Rust. The pointer seems to be twice as large as a normal pointer, but I don't understand why. It also seems to have something to do with trait objects.
pointers rust
I've read the term "fat pointer" in several contexts already, but I'm not sure what exactly it means and when it is used in Rust. The pointer seems to be twice as large as a normal pointer, but I don't understand why. It also seems to have something to do with trait objects.
pointers rust
pointers rust
asked 11 hours ago
Lukas KalbertodtLukas Kalbertodt
31.7k5 gold badges79 silver badges145 bronze badges
31.7k5 gold badges79 silver badges145 bronze badges
Note: I noticed that the Google results for "rust fat pointer" are really not great, i.e. it is not easy to quickly see what a fat pointer is. That's why I created this Q&A.
– Lukas Kalbertodt
11 hours ago
12
I personally think this is a great Q&A.
– BurntSushi5
10 hours ago
1
Thank you for the interesting post. This was very informative for a Rust noob like myself.
– Jman
7 hours ago
add a comment |
Note: I noticed that the Google results for "rust fat pointer" are really not great, i.e. it is not easy to quickly see what a fat pointer is. That's why I created this Q&A.
– Lukas Kalbertodt
11 hours ago
12
I personally think this is a great Q&A.
– BurntSushi5
10 hours ago
1
Thank you for the interesting post. This was very informative for a Rust noob like myself.
– Jman
7 hours ago
Note: I noticed that the Google results for "rust fat pointer" are really not great, i.e. it is not easy to quickly see what a fat pointer is. That's why I created this Q&A.
– Lukas Kalbertodt
11 hours ago
Note: I noticed that the Google results for "rust fat pointer" are really not great, i.e. it is not easy to quickly see what a fat pointer is. That's why I created this Q&A.
– Lukas Kalbertodt
11 hours ago
12
12
I personally think this is a great Q&A.
– BurntSushi5
10 hours ago
I personally think this is a great Q&A.
– BurntSushi5
10 hours ago
1
1
Thank you for the interesting post. This was very informative for a Rust noob like myself.
– Jman
7 hours ago
Thank you for the interesting post. This was very informative for a Rust noob like myself.
– Jman
7 hours ago
add a comment |
1 Answer
1
active
oldest
votes
The term "fat pointer" is used to refer to references and raw pointers to dynamically sized types (DSTs) – slices or trait objects. A fat pointer contains a pointer plus some information that makes the DST "complete" (e.g. the length).
Most commonly used types in Rust are not DSTs, but have a fixed size known at compile time. These types implement the Sized trait. Even types that manage a heap buffer of dynamic size (like Vec<T>) are Sized as the compiler knows the exact number of bytes a Vec<T> instance will take up on the stack. There are currently four different kinds of DSTs in Rust.
Slices ([T] and str)
The type [T] (for any T) is dynamically sized (so is the special "string slice" type str). That's why you usually only see it as &[T] or &mut [T], i.e. behind a reference. This reference is a so called "fat pointer". Let's check:
dbg!(size_of::<&u32>());
dbg!(size_of::<&[u32; 2]>());
dbg!(size_of::<&[u32]>());
This prints (with some cleanup):
size_of::<&u32>() = 8
size_of::<&[u32; 2]>() = 8
size_of::<&[u32]>() = 16
So we see that a reference to a normal type as u32 is 8 bytes large, as is a reference to an array [u32; 2]. Those two types are not DSTs. But as [u32] is a DST, the reference to it is twice as large. In the case of slices, the additional data that "completes" the DST is simply the length. So one could say the representation of &[u32] is something like this:
struct SliceRef
ptr: *const u32,
len: usize,
Trait objects (&dyn Trait)
When using traits as trait objects (i.e. type erased, dynamically dispatched), these trait objects are DSTs. Example:
trait Animal
fn speak(&self);
struct Cat;
impl Animal for Cat
fn speak(&self)
println!("meow");
dbg!(size_of::<&Cat>());
dbg!(size_of::<&dyn Animal>());
This prints (with some cleanup):
size_of::<&Cat>() = 8
size_of::<&dyn Animal>() = 16
Again, &Cat is only 8 bytes large because Cat is a normal type. But dyn Animal is a trait object and therefore dynamically sized. As such, &dyn Animal is 16 bytes large.
In the case of trait objects, the additional data that completes the DST is a pointer to the vtable (the vptr). I cannot fully explain the concept of vtables and vptrs here, but they are used to call the correct method implementation in this virtual dispatch context. The vtable is a static piece of data that basically only contains a function pointer for each method. With that, a reference to a trait object is basically represented as:
struct TraitObjectRef
data_ptr: *const (),
vptr: *const (),
(This is different from C++, where the vptr for abstract classes is stored within the object. Both approaches have advantages and disadvantages.)
Custom DSTs
It's actually possible to create your own DSTs by having a struct where the last field is a DST. This is rather rare, though. One prominent example is std::path::Path.
A reference or pointer to the custom DST is also a fat pointer. The additional data depends on the kind of DST inside the struct.
Exception: Extern types
In RFC 1861, the extern type feature was introduced. Extern types are also DSTs, but pointers to them are not fat pointers. Or more exactly, as the RFC puts it:
In Rust, pointers to DSTs carry metadata about the object being pointed to. For strings and slices this is the length of the buffer, for trait objects this is the object's vtable. For extern types the metadata is simply
(). This means that a pointer to an extern type has the same size as ausize(ie. it is not a "fat pointer").
But if you are not interacting with a C interface, you probably won't ever have to deal with these extern types.
Above, we've seen the sizes for immutable references. Fat pointers work the same for mutable references, immutable raw pointers and mutable raw pointers:
size_of::<&[u32]>() = 16
size_of::<&mut [u32]>() = 16
size_of::<*const [u32]>() = 16
size_of::<*mut [u32]>() = 16
1
Not all DST pointers are fat,extern typepointers aren't for example
– Optimistic Peach
7 hours ago
1
@OptimisticPeach Wow, TIL. Can you link some resource where I can learn more about this? Neither theSizeddocs nor the Rust Reference mentions this. I would like to add this to my answer, but need to learn a bit more first.
– Lukas Kalbertodt
7 hours ago
2
@LukasKalbertodt it's not much but may be a starting point? github.com/rust-lang/rfcs/blob/master/text/… As far as I understand it, it boils down to the "additional data that completes the DST" (quoted from your answer) being()for extern types.
– Fynn Becker
6 hours ago
1
@FynnBecker Great, thank you! I edited the answer and it should now explain this exception. Thank you two for the hint and link!
– Lukas Kalbertodt
6 hours ago
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f57754901%2fwhat-is-a-fat-pointer-in-rust%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
The term "fat pointer" is used to refer to references and raw pointers to dynamically sized types (DSTs) – slices or trait objects. A fat pointer contains a pointer plus some information that makes the DST "complete" (e.g. the length).
Most commonly used types in Rust are not DSTs, but have a fixed size known at compile time. These types implement the Sized trait. Even types that manage a heap buffer of dynamic size (like Vec<T>) are Sized as the compiler knows the exact number of bytes a Vec<T> instance will take up on the stack. There are currently four different kinds of DSTs in Rust.
Slices ([T] and str)
The type [T] (for any T) is dynamically sized (so is the special "string slice" type str). That's why you usually only see it as &[T] or &mut [T], i.e. behind a reference. This reference is a so called "fat pointer". Let's check:
dbg!(size_of::<&u32>());
dbg!(size_of::<&[u32; 2]>());
dbg!(size_of::<&[u32]>());
This prints (with some cleanup):
size_of::<&u32>() = 8
size_of::<&[u32; 2]>() = 8
size_of::<&[u32]>() = 16
So we see that a reference to a normal type as u32 is 8 bytes large, as is a reference to an array [u32; 2]. Those two types are not DSTs. But as [u32] is a DST, the reference to it is twice as large. In the case of slices, the additional data that "completes" the DST is simply the length. So one could say the representation of &[u32] is something like this:
struct SliceRef
ptr: *const u32,
len: usize,
Trait objects (&dyn Trait)
When using traits as trait objects (i.e. type erased, dynamically dispatched), these trait objects are DSTs. Example:
trait Animal
fn speak(&self);
struct Cat;
impl Animal for Cat
fn speak(&self)
println!("meow");
dbg!(size_of::<&Cat>());
dbg!(size_of::<&dyn Animal>());
This prints (with some cleanup):
size_of::<&Cat>() = 8
size_of::<&dyn Animal>() = 16
Again, &Cat is only 8 bytes large because Cat is a normal type. But dyn Animal is a trait object and therefore dynamically sized. As such, &dyn Animal is 16 bytes large.
In the case of trait objects, the additional data that completes the DST is a pointer to the vtable (the vptr). I cannot fully explain the concept of vtables and vptrs here, but they are used to call the correct method implementation in this virtual dispatch context. The vtable is a static piece of data that basically only contains a function pointer for each method. With that, a reference to a trait object is basically represented as:
struct TraitObjectRef
data_ptr: *const (),
vptr: *const (),
(This is different from C++, where the vptr for abstract classes is stored within the object. Both approaches have advantages and disadvantages.)
Custom DSTs
It's actually possible to create your own DSTs by having a struct where the last field is a DST. This is rather rare, though. One prominent example is std::path::Path.
A reference or pointer to the custom DST is also a fat pointer. The additional data depends on the kind of DST inside the struct.
Exception: Extern types
In RFC 1861, the extern type feature was introduced. Extern types are also DSTs, but pointers to them are not fat pointers. Or more exactly, as the RFC puts it:
In Rust, pointers to DSTs carry metadata about the object being pointed to. For strings and slices this is the length of the buffer, for trait objects this is the object's vtable. For extern types the metadata is simply
(). This means that a pointer to an extern type has the same size as ausize(ie. it is not a "fat pointer").
But if you are not interacting with a C interface, you probably won't ever have to deal with these extern types.
Above, we've seen the sizes for immutable references. Fat pointers work the same for mutable references, immutable raw pointers and mutable raw pointers:
size_of::<&[u32]>() = 16
size_of::<&mut [u32]>() = 16
size_of::<*const [u32]>() = 16
size_of::<*mut [u32]>() = 16
1
Not all DST pointers are fat,extern typepointers aren't for example
– Optimistic Peach
7 hours ago
1
@OptimisticPeach Wow, TIL. Can you link some resource where I can learn more about this? Neither theSizeddocs nor the Rust Reference mentions this. I would like to add this to my answer, but need to learn a bit more first.
– Lukas Kalbertodt
7 hours ago
2
@LukasKalbertodt it's not much but may be a starting point? github.com/rust-lang/rfcs/blob/master/text/… As far as I understand it, it boils down to the "additional data that completes the DST" (quoted from your answer) being()for extern types.
– Fynn Becker
6 hours ago
1
@FynnBecker Great, thank you! I edited the answer and it should now explain this exception. Thank you two for the hint and link!
– Lukas Kalbertodt
6 hours ago
add a comment |
The term "fat pointer" is used to refer to references and raw pointers to dynamically sized types (DSTs) – slices or trait objects. A fat pointer contains a pointer plus some information that makes the DST "complete" (e.g. the length).
Most commonly used types in Rust are not DSTs, but have a fixed size known at compile time. These types implement the Sized trait. Even types that manage a heap buffer of dynamic size (like Vec<T>) are Sized as the compiler knows the exact number of bytes a Vec<T> instance will take up on the stack. There are currently four different kinds of DSTs in Rust.
Slices ([T] and str)
The type [T] (for any T) is dynamically sized (so is the special "string slice" type str). That's why you usually only see it as &[T] or &mut [T], i.e. behind a reference. This reference is a so called "fat pointer". Let's check:
dbg!(size_of::<&u32>());
dbg!(size_of::<&[u32; 2]>());
dbg!(size_of::<&[u32]>());
This prints (with some cleanup):
size_of::<&u32>() = 8
size_of::<&[u32; 2]>() = 8
size_of::<&[u32]>() = 16
So we see that a reference to a normal type as u32 is 8 bytes large, as is a reference to an array [u32; 2]. Those two types are not DSTs. But as [u32] is a DST, the reference to it is twice as large. In the case of slices, the additional data that "completes" the DST is simply the length. So one could say the representation of &[u32] is something like this:
struct SliceRef
ptr: *const u32,
len: usize,
Trait objects (&dyn Trait)
When using traits as trait objects (i.e. type erased, dynamically dispatched), these trait objects are DSTs. Example:
trait Animal
fn speak(&self);
struct Cat;
impl Animal for Cat
fn speak(&self)
println!("meow");
dbg!(size_of::<&Cat>());
dbg!(size_of::<&dyn Animal>());
This prints (with some cleanup):
size_of::<&Cat>() = 8
size_of::<&dyn Animal>() = 16
Again, &Cat is only 8 bytes large because Cat is a normal type. But dyn Animal is a trait object and therefore dynamically sized. As such, &dyn Animal is 16 bytes large.
In the case of trait objects, the additional data that completes the DST is a pointer to the vtable (the vptr). I cannot fully explain the concept of vtables and vptrs here, but they are used to call the correct method implementation in this virtual dispatch context. The vtable is a static piece of data that basically only contains a function pointer for each method. With that, a reference to a trait object is basically represented as:
struct TraitObjectRef
data_ptr: *const (),
vptr: *const (),
(This is different from C++, where the vptr for abstract classes is stored within the object. Both approaches have advantages and disadvantages.)
Custom DSTs
It's actually possible to create your own DSTs by having a struct where the last field is a DST. This is rather rare, though. One prominent example is std::path::Path.
A reference or pointer to the custom DST is also a fat pointer. The additional data depends on the kind of DST inside the struct.
Exception: Extern types
In RFC 1861, the extern type feature was introduced. Extern types are also DSTs, but pointers to them are not fat pointers. Or more exactly, as the RFC puts it:
In Rust, pointers to DSTs carry metadata about the object being pointed to. For strings and slices this is the length of the buffer, for trait objects this is the object's vtable. For extern types the metadata is simply
(). This means that a pointer to an extern type has the same size as ausize(ie. it is not a "fat pointer").
But if you are not interacting with a C interface, you probably won't ever have to deal with these extern types.
Above, we've seen the sizes for immutable references. Fat pointers work the same for mutable references, immutable raw pointers and mutable raw pointers:
size_of::<&[u32]>() = 16
size_of::<&mut [u32]>() = 16
size_of::<*const [u32]>() = 16
size_of::<*mut [u32]>() = 16
1
Not all DST pointers are fat,extern typepointers aren't for example
– Optimistic Peach
7 hours ago
1
@OptimisticPeach Wow, TIL. Can you link some resource where I can learn more about this? Neither theSizeddocs nor the Rust Reference mentions this. I would like to add this to my answer, but need to learn a bit more first.
– Lukas Kalbertodt
7 hours ago
2
@LukasKalbertodt it's not much but may be a starting point? github.com/rust-lang/rfcs/blob/master/text/… As far as I understand it, it boils down to the "additional data that completes the DST" (quoted from your answer) being()for extern types.
– Fynn Becker
6 hours ago
1
@FynnBecker Great, thank you! I edited the answer and it should now explain this exception. Thank you two for the hint and link!
– Lukas Kalbertodt
6 hours ago
add a comment |
The term "fat pointer" is used to refer to references and raw pointers to dynamically sized types (DSTs) – slices or trait objects. A fat pointer contains a pointer plus some information that makes the DST "complete" (e.g. the length).
Most commonly used types in Rust are not DSTs, but have a fixed size known at compile time. These types implement the Sized trait. Even types that manage a heap buffer of dynamic size (like Vec<T>) are Sized as the compiler knows the exact number of bytes a Vec<T> instance will take up on the stack. There are currently four different kinds of DSTs in Rust.
Slices ([T] and str)
The type [T] (for any T) is dynamically sized (so is the special "string slice" type str). That's why you usually only see it as &[T] or &mut [T], i.e. behind a reference. This reference is a so called "fat pointer". Let's check:
dbg!(size_of::<&u32>());
dbg!(size_of::<&[u32; 2]>());
dbg!(size_of::<&[u32]>());
This prints (with some cleanup):
size_of::<&u32>() = 8
size_of::<&[u32; 2]>() = 8
size_of::<&[u32]>() = 16
So we see that a reference to a normal type as u32 is 8 bytes large, as is a reference to an array [u32; 2]. Those two types are not DSTs. But as [u32] is a DST, the reference to it is twice as large. In the case of slices, the additional data that "completes" the DST is simply the length. So one could say the representation of &[u32] is something like this:
struct SliceRef
ptr: *const u32,
len: usize,
Trait objects (&dyn Trait)
When using traits as trait objects (i.e. type erased, dynamically dispatched), these trait objects are DSTs. Example:
trait Animal
fn speak(&self);
struct Cat;
impl Animal for Cat
fn speak(&self)
println!("meow");
dbg!(size_of::<&Cat>());
dbg!(size_of::<&dyn Animal>());
This prints (with some cleanup):
size_of::<&Cat>() = 8
size_of::<&dyn Animal>() = 16
Again, &Cat is only 8 bytes large because Cat is a normal type. But dyn Animal is a trait object and therefore dynamically sized. As such, &dyn Animal is 16 bytes large.
In the case of trait objects, the additional data that completes the DST is a pointer to the vtable (the vptr). I cannot fully explain the concept of vtables and vptrs here, but they are used to call the correct method implementation in this virtual dispatch context. The vtable is a static piece of data that basically only contains a function pointer for each method. With that, a reference to a trait object is basically represented as:
struct TraitObjectRef
data_ptr: *const (),
vptr: *const (),
(This is different from C++, where the vptr for abstract classes is stored within the object. Both approaches have advantages and disadvantages.)
Custom DSTs
It's actually possible to create your own DSTs by having a struct where the last field is a DST. This is rather rare, though. One prominent example is std::path::Path.
A reference or pointer to the custom DST is also a fat pointer. The additional data depends on the kind of DST inside the struct.
Exception: Extern types
In RFC 1861, the extern type feature was introduced. Extern types are also DSTs, but pointers to them are not fat pointers. Or more exactly, as the RFC puts it:
In Rust, pointers to DSTs carry metadata about the object being pointed to. For strings and slices this is the length of the buffer, for trait objects this is the object's vtable. For extern types the metadata is simply
(). This means that a pointer to an extern type has the same size as ausize(ie. it is not a "fat pointer").
But if you are not interacting with a C interface, you probably won't ever have to deal with these extern types.
Above, we've seen the sizes for immutable references. Fat pointers work the same for mutable references, immutable raw pointers and mutable raw pointers:
size_of::<&[u32]>() = 16
size_of::<&mut [u32]>() = 16
size_of::<*const [u32]>() = 16
size_of::<*mut [u32]>() = 16
The term "fat pointer" is used to refer to references and raw pointers to dynamically sized types (DSTs) – slices or trait objects. A fat pointer contains a pointer plus some information that makes the DST "complete" (e.g. the length).
Most commonly used types in Rust are not DSTs, but have a fixed size known at compile time. These types implement the Sized trait. Even types that manage a heap buffer of dynamic size (like Vec<T>) are Sized as the compiler knows the exact number of bytes a Vec<T> instance will take up on the stack. There are currently four different kinds of DSTs in Rust.
Slices ([T] and str)
The type [T] (for any T) is dynamically sized (so is the special "string slice" type str). That's why you usually only see it as &[T] or &mut [T], i.e. behind a reference. This reference is a so called "fat pointer". Let's check:
dbg!(size_of::<&u32>());
dbg!(size_of::<&[u32; 2]>());
dbg!(size_of::<&[u32]>());
This prints (with some cleanup):
size_of::<&u32>() = 8
size_of::<&[u32; 2]>() = 8
size_of::<&[u32]>() = 16
So we see that a reference to a normal type as u32 is 8 bytes large, as is a reference to an array [u32; 2]. Those two types are not DSTs. But as [u32] is a DST, the reference to it is twice as large. In the case of slices, the additional data that "completes" the DST is simply the length. So one could say the representation of &[u32] is something like this:
struct SliceRef
ptr: *const u32,
len: usize,
Trait objects (&dyn Trait)
When using traits as trait objects (i.e. type erased, dynamically dispatched), these trait objects are DSTs. Example:
trait Animal
fn speak(&self);
struct Cat;
impl Animal for Cat
fn speak(&self)
println!("meow");
dbg!(size_of::<&Cat>());
dbg!(size_of::<&dyn Animal>());
This prints (with some cleanup):
size_of::<&Cat>() = 8
size_of::<&dyn Animal>() = 16
Again, &Cat is only 8 bytes large because Cat is a normal type. But dyn Animal is a trait object and therefore dynamically sized. As such, &dyn Animal is 16 bytes large.
In the case of trait objects, the additional data that completes the DST is a pointer to the vtable (the vptr). I cannot fully explain the concept of vtables and vptrs here, but they are used to call the correct method implementation in this virtual dispatch context. The vtable is a static piece of data that basically only contains a function pointer for each method. With that, a reference to a trait object is basically represented as:
struct TraitObjectRef
data_ptr: *const (),
vptr: *const (),
(This is different from C++, where the vptr for abstract classes is stored within the object. Both approaches have advantages and disadvantages.)
Custom DSTs
It's actually possible to create your own DSTs by having a struct where the last field is a DST. This is rather rare, though. One prominent example is std::path::Path.
A reference or pointer to the custom DST is also a fat pointer. The additional data depends on the kind of DST inside the struct.
Exception: Extern types
In RFC 1861, the extern type feature was introduced. Extern types are also DSTs, but pointers to them are not fat pointers. Or more exactly, as the RFC puts it:
In Rust, pointers to DSTs carry metadata about the object being pointed to. For strings and slices this is the length of the buffer, for trait objects this is the object's vtable. For extern types the metadata is simply
(). This means that a pointer to an extern type has the same size as ausize(ie. it is not a "fat pointer").
But if you are not interacting with a C interface, you probably won't ever have to deal with these extern types.
Above, we've seen the sizes for immutable references. Fat pointers work the same for mutable references, immutable raw pointers and mutable raw pointers:
size_of::<&[u32]>() = 16
size_of::<&mut [u32]>() = 16
size_of::<*const [u32]>() = 16
size_of::<*mut [u32]>() = 16
edited 6 hours ago
answered 11 hours ago
Lukas KalbertodtLukas Kalbertodt
31.7k5 gold badges79 silver badges145 bronze badges
31.7k5 gold badges79 silver badges145 bronze badges
1
Not all DST pointers are fat,extern typepointers aren't for example
– Optimistic Peach
7 hours ago
1
@OptimisticPeach Wow, TIL. Can you link some resource where I can learn more about this? Neither theSizeddocs nor the Rust Reference mentions this. I would like to add this to my answer, but need to learn a bit more first.
– Lukas Kalbertodt
7 hours ago
2
@LukasKalbertodt it's not much but may be a starting point? github.com/rust-lang/rfcs/blob/master/text/… As far as I understand it, it boils down to the "additional data that completes the DST" (quoted from your answer) being()for extern types.
– Fynn Becker
6 hours ago
1
@FynnBecker Great, thank you! I edited the answer and it should now explain this exception. Thank you two for the hint and link!
– Lukas Kalbertodt
6 hours ago
add a comment |
1
Not all DST pointers are fat,extern typepointers aren't for example
– Optimistic Peach
7 hours ago
1
@OptimisticPeach Wow, TIL. Can you link some resource where I can learn more about this? Neither theSizeddocs nor the Rust Reference mentions this. I would like to add this to my answer, but need to learn a bit more first.
– Lukas Kalbertodt
7 hours ago
2
@LukasKalbertodt it's not much but may be a starting point? github.com/rust-lang/rfcs/blob/master/text/… As far as I understand it, it boils down to the "additional data that completes the DST" (quoted from your answer) being()for extern types.
– Fynn Becker
6 hours ago
1
@FynnBecker Great, thank you! I edited the answer and it should now explain this exception. Thank you two for the hint and link!
– Lukas Kalbertodt
6 hours ago
1
1
Not all DST pointers are fat,
extern type pointers aren't for example– Optimistic Peach
7 hours ago
Not all DST pointers are fat,
extern type pointers aren't for example– Optimistic Peach
7 hours ago
1
1
@OptimisticPeach Wow, TIL. Can you link some resource where I can learn more about this? Neither the
Sized docs nor the Rust Reference mentions this. I would like to add this to my answer, but need to learn a bit more first.– Lukas Kalbertodt
7 hours ago
@OptimisticPeach Wow, TIL. Can you link some resource where I can learn more about this? Neither the
Sized docs nor the Rust Reference mentions this. I would like to add this to my answer, but need to learn a bit more first.– Lukas Kalbertodt
7 hours ago
2
2
@LukasKalbertodt it's not much but may be a starting point? github.com/rust-lang/rfcs/blob/master/text/… As far as I understand it, it boils down to the "additional data that completes the DST" (quoted from your answer) being
() for extern types.– Fynn Becker
6 hours ago
@LukasKalbertodt it's not much but may be a starting point? github.com/rust-lang/rfcs/blob/master/text/… As far as I understand it, it boils down to the "additional data that completes the DST" (quoted from your answer) being
() for extern types.– Fynn Becker
6 hours ago
1
1
@FynnBecker Great, thank you! I edited the answer and it should now explain this exception. Thank you two for the hint and link!
– Lukas Kalbertodt
6 hours ago
@FynnBecker Great, thank you! I edited the answer and it should now explain this exception. Thank you two for the hint and link!
– Lukas Kalbertodt
6 hours ago
add a comment |
Got a question that you can’t ask on public Stack Overflow? Learn more about sharing private information with Stack Overflow for Teams.
Got a question that you can’t ask on public Stack Overflow? Learn more about sharing private information with Stack Overflow for Teams.
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f57754901%2fwhat-is-a-fat-pointer-in-rust%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Note: I noticed that the Google results for "rust fat pointer" are really not great, i.e. it is not easy to quickly see what a fat pointer is. That's why I created this Q&A.
– Lukas Kalbertodt
11 hours ago
12
I personally think this is a great Q&A.
– BurntSushi5
10 hours ago
1
Thank you for the interesting post. This was very informative for a Rust noob like myself.
– Jman
7 hours ago