Comparison to C

Consider the following C code:

    void myFunction() 
    { 
        int *memblock = malloc(sizeof(int)); 
        *memblock = 256; 
        printf("%d
", *memblock); 
        free(memblock); 
    } 

Here's what the preceding code does:

  1. The int line allocates a block of memory large enough to store an integer value. The memblock variable will be in the stack, and the block of memory it points to will be in the heap.
  2. A value 256 is placed at the location pointed to by x.
  3. The value of the memory location pointed to by x is printed out.
  4. The memory allocated to memblock is deallocated.

This works well, but has the following three major drawbacks:

  • Once the memory is deallocated, it is still entirely possible to use memblock. Should you try to do this, the application will exhibit undefined behavior; most likely, the application will just quit, but there is also a chance that it will corrupt memory, which will cause a system crash. The compiler will make no attempt to warn you that you've done this, as it assumes you know what you're doing.
  • If you allocate a type larger than what was placed into the sizeof, this will also give rise to undefined behavior. You are essentially trying to put a quart into a pint pot.
  • If free is not called, the memory remains reserved, even though nothing points to it anymore, which leads to memory leaks.

You can perform something similar in Rust but, as we'll see, Rust prevents this undefined behavior automatically:

    fn myMemory() 
    { 
        let memblock: Box<i64> = Box::new(256); 
        println!("{}", memblock); 
    } 

There are a number of differences between the C and Rust code versions. They are as follows:

  • In C, you allocate heap memory with the malloc function. In Rust, we use an owned pointer via the Box<T> generic.
  • The call to malloc in C returns an int pointer (int *). In Rust, a smart pointer (Box<T>) is returned, in this case to an i64. A smart pointer is called smart as it controls when the object is freed. This can be when the pointer goes out of scope without the pointer being given away. Rust keeps track of objects and how to clean the memory up.

Another useful smart pointer type is the reference counted pointer, Rc<T>. This generic type allows the sharing of the data inside it over multiple locations. It works so that whenever the Rc binding gets cloned, a reference count is incremented. Whenever such a binding gets deallocated, the reference count is decremented. Only when the reference count reaches zero is the underlying value deallocated. Note that Rc<T> works only in single-threaded scenarios.

It is used like this:

   // 05/rc-1/src/main.rs 
use std::rc::Rc; fn main() { let memblock: Rc<i64> = Rc::new(256);
// allocate space on the heap and assign secondMethod(memblock.clone());
// clone a new reference counted pointer and pass it on to the method println!("{}", memblock);
// output the value } // free memory here fn secondMethod(memblock: Rc<i64>) { println!("In secondMethod and memblock is {}", memblock); let secMemblock: Rc<i64> = memblock.clone();
// yet another reference counted pointer to memblock }
// secMemblock goes out of scope, but the memory is not deallocated

In this code, we make several clones of the reference counted pointer. At the peak (on the second line of the secondMethod function), we have a total of three pointers to the underlying heap. When we leave secondMethod, the pointer allocated via the secMemBlock variable gets destructed. Then the memBlock clone gets deallocated. Finally, when we exit the main function, the last pointer goes away and the heap memory is deallocated.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset