I am trying to provide a default implementation of protocol so it can satisfy multiple constraints from other protocols.
Given the following protocols:
public protocol Creature {
var name: String { get }
var canMove: Bool { get }
}
public protocol Animal: Creature {}
public protocol Moveable {
var movingSpeed: Double { get set }
}
public protocol Agend {
var aged: Int { get }
}
I'm able to extend using a single condition on Self:
// all animals can move
extension Moveable where Self: Animal {
public var canMove: Bool { return true }
}
But how do I set constraints to provide a default Moveable implementation for types that conform to both Animal and Aged protocols? Something like below? Or is there some "add" "or" option for the where clause?
// Pseudocode which doesn't work
extension Moveable where Self: Animal && Self: Aged {
public var canMove: Bool { return true }
}
You could use a protocol composition:
extension Moveable where Self: protocol<Animal, Aged> {
// ...
}
Or just add the conformances one after the other:
extension Moveable where Self: Animal, Self: Aged {
// ...
}
As of the time of this post, the answer is using protocol<Animal, Aged>.
In Swift 3.0, protocol<Animal, Aged> is deprecated.
The correct usage in Swift 3.0 is:
extension Moveable where Self: Animal & Aged {
// ...
}
You can also combine the protocols with a typealias. This is useful when you are using a combination of protocols in multiple places (avoids duplication and promotes maintainability).
typealias AgedAnimal = Aged & Animal
extension Moveable where Self: AgedAnimal {
// ...
}
Since Swift 3 you can use typealias to create types that conform to multiple protocols:
typealias AgedAnimal = Animal & Aged
So your code would become:
extension Moveable where Self: AgedAnimal {
// ...
}
Or like this:
typealias Live = Aged & Moveable
extension Animal where Self: Live {
// ...
}
Make the protocol become reference type
extension Moveable: AnyObject {}
Related
This question already has answers here:
How to restrict the construction of struct?
(2 answers)
Closed 5 months ago.
I want to give some business-rule guarantees about certain structs. For example, that an EmailAddress is a valid email, or that a DateRange has a from that lies before a from, and so on. So that when passing such a value around, it is guaranteed to adhere to all business rules for that struct.
struct InvalidEmailAddress;
struct EmailAddress {
value: String
}
impl EmailAddress {
fn new(value: String) -> Result<Self, InvalidEmailAddress> {
if value.contains("#") { // I know, this isn't any sort of validation. It's an example.
Ok(Self { value })
} else {
Err(InvalidEmailAddress)
}
}
}
Ignoring that now new() behaves unexpected (it probably would be better to use a build() method), this brings an issue: When someone builds an EmailAddress through the constructor, it is guaranteed to be "valid". But when someone constructs it as normal struct, it may not be.:
let guaranteed_valid = EmailAddress::new(String::from("hi#example.com")).unwrap();
let will_crash = EmailAddress::new(String::from("localhost")).unwrap()
let unknown_valid = EmailAddress { value: String::from("hi-at-example.com") }
I would like to prohibit any users of those structs from constructing them directly like in the last line.
Is that possible at all? Are there any more ways someone could construct an EmailAddress in an invalid way?
I'm OK with placing the structs in a module, and using public/private visibility if that is possible at all. But from what I can see, any code that wants to now enforce the EmailAddress type, say a send_report(to: EmailAddress) would have access to the struct and can build it directly. Or am I missing something crucial?
You need to place your struct in a module. That way any code outside of that module will only be able to access the public functionality. Since value is not public, direct construction will not be allowed:
mod email {
#[derive(Debug)]
pub struct InvalidEmailAddress;
pub struct EmailAddress {
value: String,
}
impl EmailAddress {
pub fn new(value: String) -> Result<Self, InvalidEmailAddress> {
if value.contains("#") {
// I know, this isn't any sort of validation. It's an example.
Ok(Self { value })
} else {
Err(InvalidEmailAddress)
}
}
}
}
use email::EmailAddress;
fn main() {
let e = EmailAddress::new("foo#bar".to_string()).unwrap(); // no error
//let e = EmailAddress { value: "invalid".to_string() }; // "field `value` of struct `EmailAddress` is private"
}
Playground
More details on visibility in the Book.
#[derive(Serialize, Deserialize, Debug)]
struct Product {
id: usize,
name: String,
timestamp: i128
}
I deserialize this struct value from a JSON value.
Now I want to expose another property on my struct:
dt: OffsetDateTime
I want this property to be immutable, and set only once. So I don't want to expose a function that like below b/c it would re-calculate each time I call it:
impl Product {
fn dt(&self) -> OffsetDateTime {
OffsetDateTime::from_unix_timestamp_nanos(self.timestamp)
}
}
In java world or other languages I would do something like this:
private dt: OffsetDateTime = null;
public OffsetDateTime getDt() {
if(dt == null) {
dt = OffsetDateTime::from_unix_timestamp_nanos(self.timestamp)
}
return dt;
}
Does Rust have a similar pattern I can use?
You have three options:
Initialize it when initializing the struct, by providing a constructor. This is by far the easiest solution, if initialization isn't expensive or access is common enough that initializing always is not a problem. This is not equivalent to your Java code, however.
Store an Option<OffsetDateTime> and use Option::get_or_insert_with() to initialize it on access. This is cheapier than the third option, but requires a &mut access:
pub fn dt(&mut self) -> &OffsetDateTime {
self.dt.get_or_insert_with(|| { /* Initialization logic */ })
}
Use a library such as once_cell (or the unstable versions in std) to initialize under & access. You can use either Sync or not, depending on whether you need multiple threads to access the data):
pub fn dt(&self) -> &OffsetDateTime {
self.dt.get_or_init(|| { /* Initialization logic */ })
}
You could use an Option to simulate the Java behavior.
struct P {
pub thing: Option<i32>
}
impl P {
pub fn calc_thing( mut self ) -> i32 {
if let None = self.thing {
self.thing = Some(5);
}
self.thing.unwrap()
}
}
fn main(){
let p = P{ thing: None };
println!( "{}", p.calc_thing() );
}
struct Person{
name: String
}
fn person_factory(name: String)-> Person {
Person{
name,
}
}
fn main() {
let p1 = person_factory("Viktor Draganov".to_string());
println!("{}", p1.name);
}
Is this factory in rust? And how can I initialize person from constructor?
person_factory does look like a factory (if not a particularly useful or necessary one). Usually factories exist to abstract some creation logic away from the user. Such as converting a date of birth to an age when creating a person.
Rust does not have any special method that works like a constructor you'd be familiar with from languages like Java, Python, C++, etc; however, there is a common practice that provides essentially the same behavior as described in this site which covers Rust design patterns: Constructors.
Following this article you could make a "constructor" for your Person class like this (it should look VERY similar to the factory method you shared above):
struct Person{
name: String
}
impl Person {
pub fn new(name: String) -> Self {
Person {
name: name
}
}
}
fn main() {
let p1 = Person::new("Viktor Draganov".to_string());
println!("{}", p1.name);
}
I have an enum with a String:
enum MyLovelyEnum {
Thing(String),
}
For tests, I would like to be able to pass in a &'static str to avoid MyLovelyEnum::Thing("abc".to_string) over and over.
I found that you can do this nicely with structs with a constructor:
// From: https://hermanradtke.com/2015/05/06/creating-a-rust-function-that-accepts-string-or-str.html
struct Person {
name: String,
}
impl Person {
fn new<S: Into<String>>(name: S) -> Person {
Person { name: name.into() }
}
}
fn main() {
let person = Person::new("Herman");
let person = Person::new("Herman".to_string());
}
I know I can use lifetimes or Cow as described in What's the best practice for str/String values in Rust enums? or I can make my own function.
Is there something close to the example in the blog post for enums? e.g.
// this is the kind of thing I am after but this specifically is not correct syntax
enum MyLovelyEnum {
Thing<S: Into<String>>(S)
}
You can create a generic enum:
enum MyLovelyEnum<S>
where
S: Into<String>,
{
Thing(S),
}
MyLovelyEnum::Thing("a");
MyLovelyEnum::Thing("b".to_string());
I likely wouldn't do that in my code, instead opting to create a constructor, much like the blog post you linked:
enum MyLovelyEnum {
Thing(String),
}
impl MyLovelyEnum {
fn thing(s: impl Into<String>) -> Self {
MyLovelyEnum::Thing(s.into())
}
}
MyLovelyEnum::thing("a");
MyLovelyEnum::thing("b".to_string());
Imagine the following (common) task at hand: a cross-platform library is needed which provides access to physical devices (e.g. USB ones) and shall expose a unified API across all platforms. The following additional properties are to be satisfied:
A platform can support multiple backends to interact with a physical device.
The backend can be chosen by consumers of the library by using URIs.
One must be able to choose the backend at runtime.
Here is the common implementation part representing two concrete USB device abstractions, "A" and "B":
// only available if feature "A" is selected
struct A {}
// only available if feature "B" is selected
struct B {}
trait Common {
fn doit(&self);
}
impl Common for A {
fn doit(&self) { /* do something */ }
}
impl Common for B {
fn doit(&self) { /* do something */ }
}
The following is a naive example of using a global context struct as "factory" to solve the task:
struct Context {}
impl Context {
pub fn create<S: AsRef<str>>(_uri: S) -> Option<Box<dyn Common>> {
#[cfg(feature = "A")]
if _uri == "A" {
return Some(Box::new(A {}))
}
#[cfg(feature = "B")]
if _uri == "B" {
return Some(Box::new(B {}))
}
None
}
}
fn main() {
// pretend feature "A" has been selected
let a = Context::create("A");
}
But there is another way to do this in Rust - using a single struct which hides the implementation details but acts as a single point of contact to consumers:
struct Device {
inner: Box<dyn Common>,
}
impl Device {
pub fn new<S: AsRef<str>>(_uri: S) -> Option<Self> {
#[cfg(feature = "A")]
if _uri == "A" {
return Some(Device {
inner: Box::new(A {})
});
}
#[cfg(feature = "B")]
if _uri == "B" {
return Some(Device {
inner: Box::new(B {})
});
}
None
}
}
impl Common for Device {
fn doit(&self) { self.inner.doit() }
}
fn main() {
// pretend feature "A" has been selected
let a = Device::new("A");
}
Which one is deemed more idiomatic in Rust?
What about auto-derived traits such as Send or Sync?
For example, "A" and "B" being Send does not make Box<dyn Common> Sync automatically, instead one has to explicitly add the constraint: Box<dyn Common + Send>.
Adding such trait constraints in the first case (factory style) requires changing the function signature. In the second case, one only needs to change the internals of struct Device, the public facing API is not affected - apart from struct Device becoming e.g. Send.