Implementing behavior for types

In previous examples, we've seen what appeared to be calls to functions that were contained within data values, such as "127.0.0.1:12345".parse() or ["Hello", "world", "of", "loops"].iter(). Those are functions that have been implemented on the type of those values. Implementing functions on a type looks like this:

impl Constrained {
pub fn set(&mut self, value: i32) {
self.current = value;
}

pub fn get(&self) -> i32 {
if self.current < self.min {
return self.min;
}
else if self.current > self.max {
return self.max;
}
else {
return self.current;
};
}
}

This is an implementation block (which is not a block expression) for a data type, in this case the Constrained type that we created earlier. Implementation blocks are introduced with the impl keyword, then they have the name of the type that we want to place our functions inside of, and then the functions we want to add to the data type, between the { and } symbols.

While we can access functions that were implemented on a type as if they were variables contained in data values of that type, they're not actually stored within the memory holding the data value. There's no need for each data value to have copies of the functions, since the copies would all be identical anyway.

Functions implemented on a type can be public or private, depending on whether we want them to be used by external users of the data type, or only by other functions within the current module.

When a function is implemented on a type, the first parameter is special. It's automatically provided to the function, even though it is not passed as a function parameter when we call the function. This automatic parameter is traditionally called self. It is the job of self to give the function access to the data value that it is being called through, meaning that if we do something like "127.0.0.1".parse(), the parse function receives "127.0.0.1" as its self parameter. The self parameter can be written as self, &self, or &mut self, a choice that we'll discuss in the next chapter.

The syntax of an implementation block allows us to specify which data type we're implementing functions on. Could we implement functions on types we didn't create, such as i32 or SocketAddr? The answer is yes, but only if we create a trait. We'll see more about traits in Chapter 5One data Type Representing Multiple Kinds of Data. Without using traits, we're only able to implement functions on data types we created within the same project, although they do not have to be in the same module.

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

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