How to wrap a borrowed value in a newtype that is also a borrowed value?












1















I am trying to use the newtype pattern to wrap a pre-existing type. That inner type has a modify method which lets us work with a borrowed mutable value in a callback:



struct Val;

struct Inner(Val);

impl Inner {
fn modify<F>(&self, f: F)
where F: FnOnce(&mut Val) -> &mut Val { … }
}


Now I want to provide a very similar method on my newtype Outer, which however should not work on Vals but again a newtype wrapper WrappedVal:



struct Outer(Inner);
struct WrappedVal(Val);

impl Outer {
fn modify<F>(&self, f: F)
where
F: FnOnce(&mut WrappedVal) -> &mut WrappedVal,
{
self.0.modify(|v| f(/* ??? */));
}
}


This code is a reduced example from the original API. I don't know why the reference is returned from the closure, maybe to facilitate chaining, but it shouldn't be necessary. It takes &self because it uses internal mutability - it's a type representing a peripheral register on an embedded system



How do I get a &mut WrappedVal from a &mut Val?



I have tried various things, but all were busted by the borrow-checker. I cannot move the Val out of the mutable reference to construct a proper WrappedVal, and I couldn't get lifetimes to compile either when experimenting around with struct WrappedVal(&'? mut Val) (which I don't really want actually, since they are complicating a trait implementation).



I eventually got it to compile (see Rust playground demo) using the absolute horror of



self.0.modify(|v| unsafe {
(f((v as *mut Val as *mut WrappedVal).as_mut().unwrap()) as *mut WrappedVal as *mut Val)
.as_mut()
.unwrap()
});


but surely there must be a better way?










share|improve this question





























    1















    I am trying to use the newtype pattern to wrap a pre-existing type. That inner type has a modify method which lets us work with a borrowed mutable value in a callback:



    struct Val;

    struct Inner(Val);

    impl Inner {
    fn modify<F>(&self, f: F)
    where F: FnOnce(&mut Val) -> &mut Val { … }
    }


    Now I want to provide a very similar method on my newtype Outer, which however should not work on Vals but again a newtype wrapper WrappedVal:



    struct Outer(Inner);
    struct WrappedVal(Val);

    impl Outer {
    fn modify<F>(&self, f: F)
    where
    F: FnOnce(&mut WrappedVal) -> &mut WrappedVal,
    {
    self.0.modify(|v| f(/* ??? */));
    }
    }


    This code is a reduced example from the original API. I don't know why the reference is returned from the closure, maybe to facilitate chaining, but it shouldn't be necessary. It takes &self because it uses internal mutability - it's a type representing a peripheral register on an embedded system



    How do I get a &mut WrappedVal from a &mut Val?



    I have tried various things, but all were busted by the borrow-checker. I cannot move the Val out of the mutable reference to construct a proper WrappedVal, and I couldn't get lifetimes to compile either when experimenting around with struct WrappedVal(&'? mut Val) (which I don't really want actually, since they are complicating a trait implementation).



    I eventually got it to compile (see Rust playground demo) using the absolute horror of



    self.0.modify(|v| unsafe {
    (f((v as *mut Val as *mut WrappedVal).as_mut().unwrap()) as *mut WrappedVal as *mut Val)
    .as_mut()
    .unwrap()
    });


    but surely there must be a better way?










    share|improve this question



























      1












      1








      1








      I am trying to use the newtype pattern to wrap a pre-existing type. That inner type has a modify method which lets us work with a borrowed mutable value in a callback:



      struct Val;

      struct Inner(Val);

      impl Inner {
      fn modify<F>(&self, f: F)
      where F: FnOnce(&mut Val) -> &mut Val { … }
      }


      Now I want to provide a very similar method on my newtype Outer, which however should not work on Vals but again a newtype wrapper WrappedVal:



      struct Outer(Inner);
      struct WrappedVal(Val);

      impl Outer {
      fn modify<F>(&self, f: F)
      where
      F: FnOnce(&mut WrappedVal) -> &mut WrappedVal,
      {
      self.0.modify(|v| f(/* ??? */));
      }
      }


      This code is a reduced example from the original API. I don't know why the reference is returned from the closure, maybe to facilitate chaining, but it shouldn't be necessary. It takes &self because it uses internal mutability - it's a type representing a peripheral register on an embedded system



      How do I get a &mut WrappedVal from a &mut Val?



      I have tried various things, but all were busted by the borrow-checker. I cannot move the Val out of the mutable reference to construct a proper WrappedVal, and I couldn't get lifetimes to compile either when experimenting around with struct WrappedVal(&'? mut Val) (which I don't really want actually, since they are complicating a trait implementation).



      I eventually got it to compile (see Rust playground demo) using the absolute horror of



      self.0.modify(|v| unsafe {
      (f((v as *mut Val as *mut WrappedVal).as_mut().unwrap()) as *mut WrappedVal as *mut Val)
      .as_mut()
      .unwrap()
      });


      but surely there must be a better way?










      share|improve this question
















      I am trying to use the newtype pattern to wrap a pre-existing type. That inner type has a modify method which lets us work with a borrowed mutable value in a callback:



      struct Val;

      struct Inner(Val);

      impl Inner {
      fn modify<F>(&self, f: F)
      where F: FnOnce(&mut Val) -> &mut Val { … }
      }


      Now I want to provide a very similar method on my newtype Outer, which however should not work on Vals but again a newtype wrapper WrappedVal:



      struct Outer(Inner);
      struct WrappedVal(Val);

      impl Outer {
      fn modify<F>(&self, f: F)
      where
      F: FnOnce(&mut WrappedVal) -> &mut WrappedVal,
      {
      self.0.modify(|v| f(/* ??? */));
      }
      }


      This code is a reduced example from the original API. I don't know why the reference is returned from the closure, maybe to facilitate chaining, but it shouldn't be necessary. It takes &self because it uses internal mutability - it's a type representing a peripheral register on an embedded system



      How do I get a &mut WrappedVal from a &mut Val?



      I have tried various things, but all were busted by the borrow-checker. I cannot move the Val out of the mutable reference to construct a proper WrappedVal, and I couldn't get lifetimes to compile either when experimenting around with struct WrappedVal(&'? mut Val) (which I don't really want actually, since they are complicating a trait implementation).



      I eventually got it to compile (see Rust playground demo) using the absolute horror of



      self.0.modify(|v| unsafe {
      (f((v as *mut Val as *mut WrappedVal).as_mut().unwrap()) as *mut WrappedVal as *mut Val)
      .as_mut()
      .unwrap()
      });


      but surely there must be a better way?







      reference rust borrowing newtype






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Jan 2 at 18:53









      Shepmaster

      158k15320462




      158k15320462










      asked Jan 1 at 22:59









      BergiBergi

      377k62573903




      377k62573903
























          1 Answer
          1






          active

          oldest

          votes


















          3














          There is no safe way with your current definition, and your unsafe code is not guaranteed to be safe. There's no contract that the layout of a WrappedVal matches that of a Val, even though that's all it holds.



          Solution not using unsafe



          Don't do it. Instead, wrap the reference:



          struct WrappedVal<'a>(&'a mut Val);

          impl Outer {
          fn modify<F>(&self, f: F)
          where
          F: FnOnce(WrappedVal) -> WrappedVal,
          {
          self.0.modify(|v| f(WrappedVal(v)).0)
          }
          }


          Solution using unsafe



          You can state that your type has the same representation as the type it wraps, making the pointers compatible via repr(transparent):



          #[repr(transparent)]
          struct WrappedVal(given::Val);

          impl Outer {
          fn modify<F>(&self, f: F)
          where
          F: FnOnce(&mut WrappedVal) -> &mut WrappedVal,
          {
          self.0.modify(|v| {
          // Insert documentation why **you** think this is safe
          // instead of copy-pasting from Stack Overflow
          let wv = unsafe { &mut *(v as *mut given::Val as *mut WrappedVal) };
          let wv = f(wv);
          unsafe { &mut *(wv as *mut WrappedVal as *mut given::Val) }
          })
          }
          }


          With repr(transparent) in place, the two pointers are interchangable. I ran a quick test with Miri and your full example and didn't receive any errors, but that's not a silver bullet that I didn't mess something else up.






          share|improve this answer


























          • Thanks for mentioning repr(transparent), I didn't know about that. How should I argue that the cast is safe - surely if the values have the same memory representation, I should be able to call methods from both types on the same memory location? Do I need to assess any other aspect?

            – Bergi
            Jan 2 at 11:47











          • I had attempted the safe solution where a &mut Val is wrapped, but as hinted at in the question I couldn't get the lifetimes right. I don't want to litter my code (especially Outer) with lifetime parameters that will only be needed locally in one method for WrappedVal, am I approaching this wrong? I'm already surprised that you could omit the lifetime argument in FnOnce(WrappedVal) -> WrappedVal.

            – Bergi
            Jan 2 at 11:52








          • 1





            @Bergi yes, using lifetimes like that in a trait is beyond what Rust can currently express; generic associated types (GATs) need to be implemented first. Traits are more complicated than plain functions due to their flexibility.

            – Shepmaster
            Jan 2 at 19:00











          • @Bergi My comment inside the code is meant to dissuade future people from just copy-pasting the code without reading the rest of the answer. My belief that the code is safe comes from the usage of repr(transparent), so that's what I'd use in my comment, yeah.

            – Shepmaster
            Jan 2 at 19:11











          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%2f53999600%2fhow-to-wrap-a-borrowed-value-in-a-newtype-that-is-also-a-borrowed-value%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









          3














          There is no safe way with your current definition, and your unsafe code is not guaranteed to be safe. There's no contract that the layout of a WrappedVal matches that of a Val, even though that's all it holds.



          Solution not using unsafe



          Don't do it. Instead, wrap the reference:



          struct WrappedVal<'a>(&'a mut Val);

          impl Outer {
          fn modify<F>(&self, f: F)
          where
          F: FnOnce(WrappedVal) -> WrappedVal,
          {
          self.0.modify(|v| f(WrappedVal(v)).0)
          }
          }


          Solution using unsafe



          You can state that your type has the same representation as the type it wraps, making the pointers compatible via repr(transparent):



          #[repr(transparent)]
          struct WrappedVal(given::Val);

          impl Outer {
          fn modify<F>(&self, f: F)
          where
          F: FnOnce(&mut WrappedVal) -> &mut WrappedVal,
          {
          self.0.modify(|v| {
          // Insert documentation why **you** think this is safe
          // instead of copy-pasting from Stack Overflow
          let wv = unsafe { &mut *(v as *mut given::Val as *mut WrappedVal) };
          let wv = f(wv);
          unsafe { &mut *(wv as *mut WrappedVal as *mut given::Val) }
          })
          }
          }


          With repr(transparent) in place, the two pointers are interchangable. I ran a quick test with Miri and your full example and didn't receive any errors, but that's not a silver bullet that I didn't mess something else up.






          share|improve this answer


























          • Thanks for mentioning repr(transparent), I didn't know about that. How should I argue that the cast is safe - surely if the values have the same memory representation, I should be able to call methods from both types on the same memory location? Do I need to assess any other aspect?

            – Bergi
            Jan 2 at 11:47











          • I had attempted the safe solution where a &mut Val is wrapped, but as hinted at in the question I couldn't get the lifetimes right. I don't want to litter my code (especially Outer) with lifetime parameters that will only be needed locally in one method for WrappedVal, am I approaching this wrong? I'm already surprised that you could omit the lifetime argument in FnOnce(WrappedVal) -> WrappedVal.

            – Bergi
            Jan 2 at 11:52








          • 1





            @Bergi yes, using lifetimes like that in a trait is beyond what Rust can currently express; generic associated types (GATs) need to be implemented first. Traits are more complicated than plain functions due to their flexibility.

            – Shepmaster
            Jan 2 at 19:00











          • @Bergi My comment inside the code is meant to dissuade future people from just copy-pasting the code without reading the rest of the answer. My belief that the code is safe comes from the usage of repr(transparent), so that's what I'd use in my comment, yeah.

            – Shepmaster
            Jan 2 at 19:11
















          3














          There is no safe way with your current definition, and your unsafe code is not guaranteed to be safe. There's no contract that the layout of a WrappedVal matches that of a Val, even though that's all it holds.



          Solution not using unsafe



          Don't do it. Instead, wrap the reference:



          struct WrappedVal<'a>(&'a mut Val);

          impl Outer {
          fn modify<F>(&self, f: F)
          where
          F: FnOnce(WrappedVal) -> WrappedVal,
          {
          self.0.modify(|v| f(WrappedVal(v)).0)
          }
          }


          Solution using unsafe



          You can state that your type has the same representation as the type it wraps, making the pointers compatible via repr(transparent):



          #[repr(transparent)]
          struct WrappedVal(given::Val);

          impl Outer {
          fn modify<F>(&self, f: F)
          where
          F: FnOnce(&mut WrappedVal) -> &mut WrappedVal,
          {
          self.0.modify(|v| {
          // Insert documentation why **you** think this is safe
          // instead of copy-pasting from Stack Overflow
          let wv = unsafe { &mut *(v as *mut given::Val as *mut WrappedVal) };
          let wv = f(wv);
          unsafe { &mut *(wv as *mut WrappedVal as *mut given::Val) }
          })
          }
          }


          With repr(transparent) in place, the two pointers are interchangable. I ran a quick test with Miri and your full example and didn't receive any errors, but that's not a silver bullet that I didn't mess something else up.






          share|improve this answer


























          • Thanks for mentioning repr(transparent), I didn't know about that. How should I argue that the cast is safe - surely if the values have the same memory representation, I should be able to call methods from both types on the same memory location? Do I need to assess any other aspect?

            – Bergi
            Jan 2 at 11:47











          • I had attempted the safe solution where a &mut Val is wrapped, but as hinted at in the question I couldn't get the lifetimes right. I don't want to litter my code (especially Outer) with lifetime parameters that will only be needed locally in one method for WrappedVal, am I approaching this wrong? I'm already surprised that you could omit the lifetime argument in FnOnce(WrappedVal) -> WrappedVal.

            – Bergi
            Jan 2 at 11:52








          • 1





            @Bergi yes, using lifetimes like that in a trait is beyond what Rust can currently express; generic associated types (GATs) need to be implemented first. Traits are more complicated than plain functions due to their flexibility.

            – Shepmaster
            Jan 2 at 19:00











          • @Bergi My comment inside the code is meant to dissuade future people from just copy-pasting the code without reading the rest of the answer. My belief that the code is safe comes from the usage of repr(transparent), so that's what I'd use in my comment, yeah.

            – Shepmaster
            Jan 2 at 19:11














          3












          3








          3







          There is no safe way with your current definition, and your unsafe code is not guaranteed to be safe. There's no contract that the layout of a WrappedVal matches that of a Val, even though that's all it holds.



          Solution not using unsafe



          Don't do it. Instead, wrap the reference:



          struct WrappedVal<'a>(&'a mut Val);

          impl Outer {
          fn modify<F>(&self, f: F)
          where
          F: FnOnce(WrappedVal) -> WrappedVal,
          {
          self.0.modify(|v| f(WrappedVal(v)).0)
          }
          }


          Solution using unsafe



          You can state that your type has the same representation as the type it wraps, making the pointers compatible via repr(transparent):



          #[repr(transparent)]
          struct WrappedVal(given::Val);

          impl Outer {
          fn modify<F>(&self, f: F)
          where
          F: FnOnce(&mut WrappedVal) -> &mut WrappedVal,
          {
          self.0.modify(|v| {
          // Insert documentation why **you** think this is safe
          // instead of copy-pasting from Stack Overflow
          let wv = unsafe { &mut *(v as *mut given::Val as *mut WrappedVal) };
          let wv = f(wv);
          unsafe { &mut *(wv as *mut WrappedVal as *mut given::Val) }
          })
          }
          }


          With repr(transparent) in place, the two pointers are interchangable. I ran a quick test with Miri and your full example and didn't receive any errors, but that's not a silver bullet that I didn't mess something else up.






          share|improve this answer















          There is no safe way with your current definition, and your unsafe code is not guaranteed to be safe. There's no contract that the layout of a WrappedVal matches that of a Val, even though that's all it holds.



          Solution not using unsafe



          Don't do it. Instead, wrap the reference:



          struct WrappedVal<'a>(&'a mut Val);

          impl Outer {
          fn modify<F>(&self, f: F)
          where
          F: FnOnce(WrappedVal) -> WrappedVal,
          {
          self.0.modify(|v| f(WrappedVal(v)).0)
          }
          }


          Solution using unsafe



          You can state that your type has the same representation as the type it wraps, making the pointers compatible via repr(transparent):



          #[repr(transparent)]
          struct WrappedVal(given::Val);

          impl Outer {
          fn modify<F>(&self, f: F)
          where
          F: FnOnce(&mut WrappedVal) -> &mut WrappedVal,
          {
          self.0.modify(|v| {
          // Insert documentation why **you** think this is safe
          // instead of copy-pasting from Stack Overflow
          let wv = unsafe { &mut *(v as *mut given::Val as *mut WrappedVal) };
          let wv = f(wv);
          unsafe { &mut *(wv as *mut WrappedVal as *mut given::Val) }
          })
          }
          }


          With repr(transparent) in place, the two pointers are interchangable. I ran a quick test with Miri and your full example and didn't receive any errors, but that's not a silver bullet that I didn't mess something else up.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Jan 2 at 19:10

























          answered Jan 1 at 23:52









          ShepmasterShepmaster

          158k15320462




          158k15320462













          • Thanks for mentioning repr(transparent), I didn't know about that. How should I argue that the cast is safe - surely if the values have the same memory representation, I should be able to call methods from both types on the same memory location? Do I need to assess any other aspect?

            – Bergi
            Jan 2 at 11:47











          • I had attempted the safe solution where a &mut Val is wrapped, but as hinted at in the question I couldn't get the lifetimes right. I don't want to litter my code (especially Outer) with lifetime parameters that will only be needed locally in one method for WrappedVal, am I approaching this wrong? I'm already surprised that you could omit the lifetime argument in FnOnce(WrappedVal) -> WrappedVal.

            – Bergi
            Jan 2 at 11:52








          • 1





            @Bergi yes, using lifetimes like that in a trait is beyond what Rust can currently express; generic associated types (GATs) need to be implemented first. Traits are more complicated than plain functions due to their flexibility.

            – Shepmaster
            Jan 2 at 19:00











          • @Bergi My comment inside the code is meant to dissuade future people from just copy-pasting the code without reading the rest of the answer. My belief that the code is safe comes from the usage of repr(transparent), so that's what I'd use in my comment, yeah.

            – Shepmaster
            Jan 2 at 19:11



















          • Thanks for mentioning repr(transparent), I didn't know about that. How should I argue that the cast is safe - surely if the values have the same memory representation, I should be able to call methods from both types on the same memory location? Do I need to assess any other aspect?

            – Bergi
            Jan 2 at 11:47











          • I had attempted the safe solution where a &mut Val is wrapped, but as hinted at in the question I couldn't get the lifetimes right. I don't want to litter my code (especially Outer) with lifetime parameters that will only be needed locally in one method for WrappedVal, am I approaching this wrong? I'm already surprised that you could omit the lifetime argument in FnOnce(WrappedVal) -> WrappedVal.

            – Bergi
            Jan 2 at 11:52








          • 1





            @Bergi yes, using lifetimes like that in a trait is beyond what Rust can currently express; generic associated types (GATs) need to be implemented first. Traits are more complicated than plain functions due to their flexibility.

            – Shepmaster
            Jan 2 at 19:00











          • @Bergi My comment inside the code is meant to dissuade future people from just copy-pasting the code without reading the rest of the answer. My belief that the code is safe comes from the usage of repr(transparent), so that's what I'd use in my comment, yeah.

            – Shepmaster
            Jan 2 at 19:11

















          Thanks for mentioning repr(transparent), I didn't know about that. How should I argue that the cast is safe - surely if the values have the same memory representation, I should be able to call methods from both types on the same memory location? Do I need to assess any other aspect?

          – Bergi
          Jan 2 at 11:47





          Thanks for mentioning repr(transparent), I didn't know about that. How should I argue that the cast is safe - surely if the values have the same memory representation, I should be able to call methods from both types on the same memory location? Do I need to assess any other aspect?

          – Bergi
          Jan 2 at 11:47













          I had attempted the safe solution where a &mut Val is wrapped, but as hinted at in the question I couldn't get the lifetimes right. I don't want to litter my code (especially Outer) with lifetime parameters that will only be needed locally in one method for WrappedVal, am I approaching this wrong? I'm already surprised that you could omit the lifetime argument in FnOnce(WrappedVal) -> WrappedVal.

          – Bergi
          Jan 2 at 11:52







          I had attempted the safe solution where a &mut Val is wrapped, but as hinted at in the question I couldn't get the lifetimes right. I don't want to litter my code (especially Outer) with lifetime parameters that will only be needed locally in one method for WrappedVal, am I approaching this wrong? I'm already surprised that you could omit the lifetime argument in FnOnce(WrappedVal) -> WrappedVal.

          – Bergi
          Jan 2 at 11:52






          1




          1





          @Bergi yes, using lifetimes like that in a trait is beyond what Rust can currently express; generic associated types (GATs) need to be implemented first. Traits are more complicated than plain functions due to their flexibility.

          – Shepmaster
          Jan 2 at 19:00





          @Bergi yes, using lifetimes like that in a trait is beyond what Rust can currently express; generic associated types (GATs) need to be implemented first. Traits are more complicated than plain functions due to their flexibility.

          – Shepmaster
          Jan 2 at 19:00













          @Bergi My comment inside the code is meant to dissuade future people from just copy-pasting the code without reading the rest of the answer. My belief that the code is safe comes from the usage of repr(transparent), so that's what I'd use in my comment, yeah.

          – Shepmaster
          Jan 2 at 19:11





          @Bergi My comment inside the code is meant to dissuade future people from just copy-pasting the code without reading the rest of the answer. My belief that the code is safe comes from the usage of repr(transparent), so that's what I'd use in my comment, yeah.

          – Shepmaster
          Jan 2 at 19:11




















          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%2f53999600%2fhow-to-wrap-a-borrowed-value-in-a-newtype-that-is-also-a-borrowed-value%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

          Can a sorcerer learn a 5th-level spell early by creating spell slots using the Font of Magic feature?

          Does disintegrating a polymorphed enemy still kill it after the 2018 errata?

          A Topological Invariant for $pi_3(U(n))$