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;








22















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.










share|improve this question
























  • 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

















22















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.










share|improve this question
























  • 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













22












22








22


1






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.










share|improve this question














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






share|improve this question













share|improve this question











share|improve this question




share|improve this question










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

















  • 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












1 Answer
1






active

oldest

votes


















25















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 a usize (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





share|improve this answer






















  • 1





    Not all DST pointers are fat, extern type pointers 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 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





    @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










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
);



);













draft saved

draft discarded


















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









25















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 a usize (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





share|improve this answer






















  • 1





    Not all DST pointers are fat, extern type pointers 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 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





    @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















25















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 a usize (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





share|improve this answer






















  • 1





    Not all DST pointers are fat, extern type pointers 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 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





    @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













25














25










25









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 a usize (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





share|improve this answer















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 a usize (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






share|improve this answer














share|improve this answer



share|improve this answer








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 type pointers 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 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





    @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





    Not all DST pointers are fat, extern type pointers 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 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





    @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








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.



















draft saved

draft discarded
















































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.




draft saved


draft discarded














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





















































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







Popular posts from this blog

Canceling a color specificationRandomly assigning color to Graphics3D objects?Default color for Filling in Mathematica 9Coloring specific elements of sets with a prime modified order in an array plotHow to pick a color differing significantly from the colors already in a given color list?Detection of the text colorColor numbers based on their valueCan color schemes for use with ColorData include opacity specification?My dynamic color schemes

Invision Community Contents History See also References External links Navigation menuProprietaryinvisioncommunity.comIPS Community ForumsIPS Community Forumsthis blog entry"License Changes, IP.Board 3.4, and the Future""Interview -- Matt Mecham of Ibforums""CEO Invision Power Board, Matt Mecham Is a Liar, Thief!"IPB License Explanation 1.3, 1.3.1, 2.0, and 2.1ArchivedSecurity Fixes, Updates And Enhancements For IPB 1.3.1Archived"New Demo Accounts - Invision Power Services"the original"New Default Skin"the original"Invision Power Board 3.0.0 and Applications Released"the original"Archived copy"the original"Perpetual licenses being done away with""Release Notes - Invision Power Services""Introducing: IPS Community Suite 4!"Invision Community Release Notes

François Viète Contents Biography Work and thought Bibliography See also Notes Further reading External links Navigation menup. 21Google Bookspp. 75–77Google BooksDe thou (from University of Saint Andrews)ArchivedGoogle BooksGoogle BooksGoogle BooksGoogle booksGoogle Bookscc-parthenay.frL'histoire universelle (fr)Universal History (en)ArchivedAdsabs.harvard.eduPagesperso-orange.frArchive.orgChikara Sasaki. Descartes' mathematical thought p.259Google BooksGoogle BooksGoogle Bookspp. 152 and onwardGoogle BooksGoogle BooksScribd.comGoogle Books1257-7979Google BooksGoogle BooksGoogle BooksGoogle BooksGoogle BooksGoogle BooksGallica.bnf.frGoogle BooksGoogle Books"François Viète"Francois Viète: Father of Modern Algebraic NotationThe Lawyer and the GamblerAbout TarporleySite de Jean-Paul GuichardL'algèbre nouvelle"About the Harmonicon"cb120511976(data)1188044800000 0001 0913 5903n82164680ola2013766880073431702w6vt1sb70287374827140948071409480