Rust supports DCI

56 views
Skip to first unread message

Lund Soltoft

unread,
Feb 10, 2025, 9:18:46 AMFeb 10
to object-co...@googlegroups.com
I recently looked into rust at work and got curious about the generative programming capabilities of the language. In general I've been fairly positively surprised with the language and ecosystem. 

I've not yet completed the money transfer example or any more complex. So far I've only implemented an Account context where a ledger and an int plays the only roles but I'm confident in claiming that Rust can support DCI based on a macro.
This is what the Account looks like: 

#[five::context]
pub mod account {
pub trait LedgerContract : {
fn push(&mut self, entry: LedgerEntry);
fn as_vec(&self) -> Vec<LedgerEntry>;
}

trait LedgerRole : LedgerContract{
fn add(&mut self, entry: LedgerEntry){
self.push(entry.clone());
self.log(entry.message());
}
fn log(&self, msg: String) {
println!("{}",msg);
}
}

struct Context {
ledger : LedgerRole,
account_no: i64
}
impl Context {
fn deposit(&mut self, message : String, amount: i32){
self.ledger.add(LedgerEntry::Deposit(message,amount))
}
fn withdraw(&mut self, message : String, amount: i32){
self.ledger.add(LedgerEntry::Withdrawal(message,amount))
}
fn balance(&self) -> i32 {
self.ledger.as_vec().iter().map(|entry| match entry {
LedgerEntry::Deposit(_, amount) => *amount,
LedgerEntry::Withdrawal(_, amount) => -*amount,
}).sum()
}
}
}

The idea of the macro is that any trait that ends in Role is a role and any trait that ends in Contract is a Role contract for the corresponding role. The context struct and impl is where the publicly available methods of the context are defined and those methods have access to the role methods as well as those of the contract. 

Using this would look something like this:

use account::Account;
let mut account = account::bind(ledger, 67676555);
account.deposit(String::from("Deposit 1"), 100);
account.withdraw(String::from("Withdrawal 1"), 50);
account.deposit(String::from("Deposit 2"), 200);
account.withdraw(String::from("Withdrawal 2"), 100);
println!("Balance: {}", account.balance());

The second line being the one where the players are bound to their roles. The macro uses the same rewrite "trick" as always, so if I've implemented the macro "magic" correctly there's no identity issues. 
That being said identity is actually at the forefront of rust memory management, and it's very clear in the code whether you clone/copy an object (not preserving identity) or you borrow the object (preserving identity)

The result of the above would be:

Deposit 1
Withdrawal 1
Deposit 2
Withdrawal 2
Balance: 150


Design choices:

Aside from the Contracts nothing in the module is public. The rewriting of the module creates a bind function and a trait that's publicly available. The trait being named after the module and describing the methods for the Context struct. Anything you see in the account module above is rewritten or removed completely.


Matthew Browne

unread,
Aug 26, 2025, 5:36:22 AM (11 days ago) Aug 26
to object-co...@googlegroups.com
Hi Rune,
I missed this when you originally posted it—nice implementation. Not sure if you saw my earlier post about Rust in DCI, using the type system to implement it in a way that has similarities to Cope's C++ implementation:


I don't see the definition of your macro in what you posted, so I can't comment on tradeoffs are between the approaches (and I barely know any Rust syntax anyway), but doing it based on a naming convention ending with "Role" as you have done seems like a promising approach that would be intuitive to use. If anyone is interested in taking one of these approaches and running with it a bit, we could add Rust to this page on the wiki. There's a lot you can do in Rust.

BTW, I only started looking at this back when I had some time over the winter, but I think Kotlin is another promising language for DCI: not because of the language itself (although the language has some interesting features), but because of its flexible compiler and compiler infrastructure, which supports so many different targets including compile-to-JS, compile-to-web-assembly, and compile-to-native (via LLVM).

Cheers,
Matt

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To unsubscribe from this group and stop receiving emails from it, send an email to object-composit...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/object-composition/CAGLi4Wjwr2jQYuurx56tNSj5SB3fPgq8810QrrHz2Sc8%3DNkmQA%40mail.gmail.com.
Reply all
Reply to author
Forward
0 new messages