Skip to content

Latest commit

 

History

History
 
 

1_1_default_clone_copy

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

Step 1.1: Default values, cloning and copying

Default values

Rust has a standard way to deal with default values of a type via Default trait. Read through its official docs to understand the design.

It can be auto-derived, but only for a struct whose all members have Default implementations. It is implemented for a great many types in the standard library, and also used in a surprising number of places. So if your type has a value that can be construed as being "default", it is a good idea to implement this trait.

If you're not satisfied with std deriving capabilities for Default, consider using smart-default crate. An example is quite self-explanatory:

#[derive(SmartDefault)]
enum Foo {
    Bar,
    #[default]
    Baz {
        #[default = 12]
        a: i32,
        b: i32,
        #[default(Some(Default::default()))]
        c: Option<i32>,
        #[default(_code = "vec![1, 2, 3]")]
        d: Vec<u32>,
        #[default = "four"]
        e: String,
    },
    Qux(i32),
}

A great thing that having a Default implementation you can instantiate your struct with only the non-default values and have all other fields filled with default values:

let x = Foo { bar: baz, ..Default::default() };

Cloning and copying

By default, all types in Rust follow 'move semantics'.

If you need a duplicate of a value, then its type should implement Clone trait (see official docs), and a duplicate is created by calling Clone methods explicitly. Cloning can be either cheap or expensive operation depending on type semantics.

However, Copy marker trait (see official docs) enables 'copy semantics' for a type, so a value is copied implicitly every time is passed. That's why copying must always perform a simple bit-to-bit copy operation.

Official Copy docs are quite explanatory about which types should be Copy and which types cannot:

Some types can't be copied safely. For example, copying &mut T would create an aliased mutable reference. Copying String would duplicate responsibility for managing the String's buffer, leading to a double free.

Generalizing the latter case, any type implementing Drop can't be Copy, because it's managing some resource besides its own size_of::<T> bytes.

Generally speaking, if your type can implement Copy, it should. Keep in mind, though, that implementing Copy is part of the public API of your type. If the type might become non-Copy in the future, it could be prudent to omit the Copy implementation now, to avoid a breaking API change.

For better understanding the topic, read through:

Task

Estimated time: 1 day

  • Create a Point type which represents a 2D point (x and y coordinates). This type has to be Copy and Default.
  • Create a Polyline type which represents a non-empty collection(whichever you want) of Points of unknown size. This type has to be Clone and non-Default.
    • Polyline must implement basic collection methods: addition of an item, removal of an item, getting an item. You may add additional methods & features if you desire.

Questions

After completing everything above, you should be able to answer (and understand why) the following questions:

  • What purpose does Default trait serve in Rust?
  • What is #[derive(Default)] from std capable of? What does it wrong? Which are alternatives?
  • What does Clone mean semantically?
  • What does Copy mean semantically? How is it connected with Clone? Which limitations does it have and why?